线程池状态和使用注意点
線程池的狀態
- RUNNING:接收新的任務,執行阻塞隊列中的任務
- SHUTDOWN:不接受新任務,但是仍然可以處理阻塞隊列中的任務
- STOP:不接受新任務,不處理阻塞隊列中的任務,中斷正在處理的任務
- TIDYING:所有任務已終止,workerCount(工作線程數等于0),進入TIDYING狀態將會執行terminated()方法
- TERMINATED:terminated()方法執行完成后進入TERMINATED狀態
線程池狀態轉換如下:
-
RUNNING:能接受新提交的任務,并且也能處理阻塞隊列中的任務;
-
SHUTDOWN:關閉狀態,不再接受新提交的任務,但卻可以繼續處理阻塞隊列中已保存的任務。
-
STOP:不能接受新任務,也不處理隊列中的任務,會中斷正在處理任務的線程。在線程池處于 RUNNING 或 SHUTDOWN 狀態時,調用shutdownNow() 方法會使線程池進入到該狀態;
-
TIDYING:如果所有的任務都已終止了,workerCount (有效線程數) 為0,線程池進入該狀態后會調用 terminated() 方法進入TERMINATED 狀態。
-
TERMINATED:在terminated()方法執行完后進入該狀態。?
各運行狀態的表示方式:
// runState is stored in the high-order bits private static final int RUNNING = -1 << COUNT_BITS; private static final int SHUTDOWN = 0 << COUNT_BITS; private static final int STOP = 1 << COUNT_BITS; private static final int TIDYING = 2 << COUNT_BITS; private static final int TERMINATED = 3 << COUNT_BITS; //原子變量 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));public void execute(Runnable command) {//判斷提交的Runnable 任務,如果為null,則報NullPointerExceptionif (command == null)throw new NullPointerException(); int c = ctl.get();//判斷當前線程數是否小于核心線程數,如果小于,那就調用addWorker方法新增一個Worker,也可以理解成一個線程if (workerCountOf(c) < corePoolSize) {//addWorker這個方法主要要做的事就是執行command,同時第二個參數決定以哪個界限來進行是否新增線程的判斷,傳入true則代表以核心線程數為判斷條件if (addWorker(command, true))return;c = ctl.get();}//走到這步邏輯,則說明線程數大于等于核心線程數,或者addWorker方法調用失敗了,這時就判斷線程池是否是Running狀態,如果是就調用offer方法提交線程任務到任務隊列中if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();//如果線程池狀態不是Running,說明線程池已經被關閉,這時就移除新提交到隊列中的任務if (! isRunning(recheck) && remove(command))//執行拒絕策略reject(command);//檢查下當前線程數是不是為0,為0的話就沒有線程執行任務了 else if (workerCountOf(recheck) == 0)//所以就通過addWorker新建一個線程addWorker(null, false);}//走到這步邏輯,要么是線程池狀態不是Running,說明已經關閉了,要么就是添加任務進任務隊列時失敗了,說明任務隊列滿了,這時候就該添加最大線程數了,傳入false則代表以最大線程數為判斷條件else if (!addWorker(command, false))//以上addWorker方法如果返回結果是false,就會執行拒絕策略了reject(command);}內置的四種線程池
- SingleThreadExecutor:單線程化的Executor
- FiexedThreadPool:固定數目線程的線程池(隊列數沒有限制)
- CachedThreadPool:可緩存的線程池(線程數沒有限制)
- ScheduledThreadPool:支持定時及周期性的任務執行的線程池,多數情況下可用來替代Timer類
這4個線程池都可能存在問題,不建議直接使用,建議使用自定義參數的線程池
線程池中重要的配置
- corePoolSize?: 核心線程數量
- workQueue?: 等待隊列
- maximumPoolSize?: 最大線程數量
提交任務時,判斷的順序為?corePoolSize --> workQueue -->maximumPoolSize
當線程數小于核心線程數時,創建核心線程
當線程大于等于核心線程數,且任務隊列未滿時,將任務放入隊列
當線程數大于核心線程數,且任務隊列已滿時,檢查最大線程數是否已滿,若未滿,創建非核心線程,若滿,根據拒絕策略拋出異常拒絕任務。
?
拒絕策略—RejectedExecutionHandler
- AbortPolicy?: 直接拋出異常,這是默認策略
- CallerRunsPolicy?: 用調用者所在線程來執行任務
- DiscardOldestPolicy?: 丟棄阻塞隊列中最靠前的任務,并執行當前任務
- DiscardPolicy?: 直接丟棄任務
?
遇到的坑!!!
- 任務提交后長時間沒有執行
1.任務進入了隊列,線程還在執行之前的任務。提交的任務還在排隊等待執行中
- 線程執行任務中無故消失
? ? ? ? ? 1.? 線程拒絕策略配置為CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy時 ,線程池滿了不會拋出異常。建議將拒絕策略配置為AbortPolicy
? ? ? ? ? 2.? 一般情況下,代碼只會去捕捉Exception,如果拋出Error(比如內存溢出)則會導致線程退出,而異常信息又沒有拿到。最佳的解決辦法是給線程池設置UncaughtExceptionHandler
?
總結
以上是生活随笔為你收集整理的线程池状态和使用注意点的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Executor家族的辨析
- 下一篇: ThreadLocal的两种用法