java線程池
1、以下是ThreadPoolExecutor參數完備構造方法:
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,threadFactory threadFactory, RejectedExecutionHandler handler);
corePoolSize:線程池的大小,當剛開始創建線程池時,線程數為0,當線程池中線程數量多於corePoolSize時會存於緩存隊列
maxinumPoolSize:線程池容納的最多線程數量
keepAliveTime:空閑線程的存活時間,一般情況下是當前線程線程數超過線程池大小,才會進行回收
unit:keepAliveTime的事件單位
workQueue:緩存隊列
ArrayBlockingQueue:基於數組的先進先出隊列,此隊列創建時必須指定大小;
LinkedBlockingQueue:基於鏈表的先進先出隊列,如果創建時沒有指定此隊列大小,則默認為Integer.MAX_VALUE;
synchronousQueue:這個隊列比較特殊,它不會保存提交的任務,而是將直接新建一個線程來執行新來的任務。
threadFactory:通過這個參數你可以自定義如何創建線程,例如你可以給線程指定一個有意義的名字。
handler:通過這個參數你可以自定義任務的拒絕策略。如果線程池中所有的線程都在忙碌,並且工作隊列也滿了(前提是工作隊列是有界隊列),那么此時提交任務,線程池就會拒絕接收。至於拒絕的策略,你可以通過 handler 這個參數來指定。
ThreadPoolExecutor 已經提供了以下 4 種策略。
CallerRunsPolicy:提交任務的線程自己去執行該任務。
AbortPolicy:默認的拒絕策略,會 throws RejectedExecutionException。
DiscardPolicy:直接丟棄任務,沒有任何異常拋出。
DiscardOldestPolicy:丟棄最老的任務,其實就是把最早進入工作隊列的任務丟棄,然后把新任務加入到工作隊列。
執行流程:
線程池創建線程,會判斷當前線程數是否大於corePoolSize。
如果大於則存在緩存隊列,緩沖隊列存滿后會繼續創建線程直到maximumPoolSize,拋出拒絕的異常。
如果小於則創建線程,執行任務,執行完后會從緩存隊列中取任務再執行
2、封裝線程池
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } //創建的線程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } //將corePoolSize和maximumPoolSize都設置為1,也使用的LinkedBlockingQueue
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } //將corePoolSize設置為0,將maximumPoolSize設置為Integer.MAX_VALUE,使用的SynchronousQueue,也就是說來了任務就創建線程運行,當線程空閑超過60秒,就銷毀線程
日常工作中不建議使用以上線程池:
1)newFixedThreadPool 和 newSingleThreadExecutor:主要問題是堆積的請求處理隊列可能會耗費非常大的內存,甚至 OOM。
2)newCachedThreadPool 和 newScheduledThreadPool:主要問題是線程數最大數是 Integer.MAX_VALUE,可能會創建數量非常多的線程,甚至 OOM。
創建多少線程合適呢?
創建多少線程合適,要看多線程具體的應用場景。我們的程序一般都是 CPU 計算和 I/O 操作交叉執行的,由於 I/O 設備的速度相對於 CPU 來說都很慢,所以大部分情況下,I/O 操作執行的時間相對於 CPU 計算來說都非常長,這種場景我們一般都稱為 I/O 密集型計算;和 I/O 密集型計算相對的就是 CPU 密集型計算了,CPU 密集型計算大部分場景下都是純 CPU 計算。I/O 密集型程序和 CPU 密集型程序,計算最佳線程數的方法是不同的。
對於CPU密集型來說,多線程主要目的是提成CPU利用率,保持和CPU核數一致即可。不過在工程上,,線程的數量一般會設置為“CPU 核數 +1”,這樣的話,當線程因為偶爾的內存頁失效或其他原因導致阻塞時,這個額外的線程可以頂上,從而保證 CPU 的利用率。
對於IO密集型來說,一般是最佳線程數 =CPU 核數 * [ 1 +(I/O 耗時 / CPU 耗時)]
總之上述僅可代表項目初始時設定的線程數量,后續隨着實際應用場景進行調整優化