1. 降低資源消耗。通過重復利用已創建的線程降低創建線程和銷毀造成的消耗 2. 提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。 3. 提高線程的可管理性。


ThreadPoolExecutor執行executor方法分4中情況:
1. 當前運行的線程少于corePoolSize,則創新線程來執行任務,這一步驟需要獲取全局鎖 2. 如果運行的線程大于或等于corePoolSize,則將任務加入到BlockingQueue。 3. 如果無法將任務加入BlockingQueue,則創建新的線程來處理任務,這一步驟需要獲取全局鎖 4. 如果創建新線程將使當前運行的線程超出maximumPoolSize,任務將被拒絕,并調用RejectExecutionHandler.rejectExecution()方法
詳細的拒絕規則如下:
AbortPolicy 為java線程池默認的阻塞策略,不執行此任務,而且直接拋出一個運行時異常,切記ThreadPoolExecutor.execute需要try catch,否則程序會直接退出。 DiscardPolicy 直接拋棄,任務不執行,空方法 DiscardOldestPolicy 從隊列里面拋棄head的一個任務,并再次execute 此task。 CallerRunsPolicy 在調用execute的線程里面執行此command,會阻塞入口
ThreadPoolExecutor采取上述步驟的總體設計思想,是為了在執行executor方法時,盡可能地避免獲取全局鎖。在ThreadPoolSize完成預熱之后(當前運行的線程數大于或者等于corePoolSize),幾乎所有的executor方法調用都是執行步驟2,而步驟2不需要獲取全局鎖
構造方法如下:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }構造函數的參數詳解如下:
int corePoolSize:線程池的基本大小,當提交一個任務到線程池時,線程池會創建一個線程來執行任務,即使其他空閑的基本線程能夠執行新任務也會創建線程,等到需要執行的任務數大于corePoolSize大小時,就不會再創建。如果調用了線程池的PRestartAllCoreThreads()方法,線程池會提前創建并啟動所有基本線程。
int maximumPoolSize:線程池最大數量。如果隊列滿了,并且已創建的線程數小于最大線程數,則線程會再創建新的線程執行任務。需要注意的是,如果使用了無界隊列,那么這個參數就沒有作用了
long keepAliveTime:線程活動保持時間。線程池的工作線程空閑后,保持存活的時間。所以如果任務很多,并且每個任務執行的時間比較短,可以調大時間,提高線程的利用率。 TimeUnit unit:線程活動保持時間的單位,可選的單位有天(DAYS)、小時(HOURS)、分鐘(MINUTES)、秒(SECONDS)、毫秒(MILLISECODENS)、微秒(MICROSECONDS)、納秒(NANOSECONDS)BlockingQueue<Runnable> workQueue :任務隊列,用于保存等待執行的任務的阻塞隊列。可選擇的阻塞隊列如下:
ArrayBlockingQueue:是一個基于數組結構的有界阻塞隊列,按FIFO排序。
LinkedBlockingQueue:一個基于鏈表結構的阻塞隊列,按FIFO排列,吞吐量高于ArrayBlockingQueue。靜態工廠方法Executors.newFixedThreadPool()使用了這個隊列
SynchronousQueue:一個不存儲元素的阻塞隊列。每個插入操作必須等到另一線程調用移除操作,否則插入操作一直處于阻塞狀態。吞吐量通常高于LinkedBlockingQueue,靜態工廠方法Executors.newCachedThreadPool使用了這個隊列。
ThreadFactory threadFactory:用于設置創建線程的工廠,可以通過線程工廠給每個創建出來的線程設置名字。使用開源框架guava提供的ThreadFactoryBuilder可以快速給線程池里的線程設置名字。
RejectedExecutionHandler handler:飽和策略。當隊列和線程池都滿了,說明線程池處于飽和狀態,那么必須采取一種策略出來提交的新任務。默認的AbortPolicy,表示無法處理新任務時拋出異常。也可以根據應用場景實現RejectedExecutionHandler接口自定義策略。
可以使用兩個方法向線程池提交任務:execute方法和submit方法。executre方法由于提交不需要返回值的任務,所以無法判斷任務是否被線程池執行成功。submit方法用于提交需要返回值的任務。線程池會返回一個future類型的對象,通過future對象可以判斷任務是否執行成功,并且可以通過future的get()方法來獲取返回的值,get()方法會阻塞當前線程直到任務完成,而使用get(long timeout,TimeUnit unit)方法則會阻塞當前線程一段時間后立即返回,這個時候有可能任務還未執行完。
向線程池提交任務的executor()方法
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); // 獲取線程池控制狀態 int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { // worker數量小于corePoolSize if (addWorker(command, true)) // 添加worker // 成功則返回 return; // 不成功則再次獲取線程池控制狀態 c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { // 線程池處于RUNNING狀態,將命令(用戶自定義的Runnable對象)添加進workQueue隊列 // 再次檢查,獲取線程池控制狀態 int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) // 線程池不處于RUNNING狀態,將命令從workQueue隊列中移除 // 拒絕執行命令 reject(command); else if (workerCountOf(recheck) == 0) // worker數量等于0 // 添加worker addWorker(null, false); } else if (!addWorker(command, false)) // 添加worker失敗 // 拒絕執行命令 reject(command); }3個步驟:
1. 如果運行的線程小于corePoolSize,則嘗試使用用戶定義的Runnalbe對象創建一個新的線程調用addWorker函數會原子性的檢查runState和workCount,通過返回false來防止在不應 該添加線程時添加了線程.
2. 如果一個任務能夠成功入隊列,在添加一個線城時仍需要進行雙重檢查(因為在前一次檢查后 該線程死亡了),或者當進入到此方法時,線程池已經shutdown了,所以需要再次檢查狀態, 若有必要,當停止時還需要回滾入隊列操作,或者當線程池沒有線程時需要創建一個新線程
3. 如果無法入隊列,那么需要增加一個新線程,如果此操作失敗,那么就意味著線程池已經shutdown或者已經飽和了,所以拒絕任務
線程池中的線程執行任務分兩種情況: (1) 在executor()方法中創建一個線程時,會讓這個線程執行當前任務 (2) 在線程池中創建了一個線程,然后這個線程反復從BlockingQueue獲取任務執行
可以通過調用線程池的shutdown或shutdownNow方法來關閉線程池,原理如下:
遍歷線程池中的工作線程,然后逐個調用線程的interrupt方法來中斷線程,所以無法響應中斷的任務可能永遠無法終止。
shutdownNow與shutdown的區別:
shutdowNow首先將線程池的狀態設置成STOP,然后嘗試停止所有的正在執行或者暫停任務的線程,并返回等待執行任務的列表,shutdown只是將線程池的狀態設置成SHUTDOWN狀態,然后中斷所有沒有正在執行任務的線程。
通常調用shutdown方法來關閉線程,如果任務不一定要執行完,可以調用shutdowNow方法。
CPU密集型:應配置盡可能小的線程,如N+1(N為cpu個數)
IO密集型:應配置盡可能多的線程,如2*N(N為cpu個數)
混合型:如果可以拆分,可以拆分成CPU密集型和IO密集型
優先級不同的任務:可以使用PriorityBlockingQueue隊列
可以通過Runtime.getRuntime().availableProcessors()獲取CPU個數
新聞熱點
疑難解答