在日常使用執行緒池的過程中,怎樣合理地關閉執行緒池,最小程度地影響業務,shutdown 和 shutdownNow 該如何選擇?
1 public void shutdown() { 2 final ReentrantLock mainLock = this.mainLock; 3 mainLock.lock(); 4 try { 5 checkShutdownAccess(); 6 advanceRunState(SHUTDOWN); 7 interruptIdleWorkers(); 8 onShutdown(); // hook for ScheduledThreadPoolExecutor 9 } finally { 10 mainLock.unlock(); 11 } 12 tryTerminate(); 13 }
1 public List<Runnable> shutdownNow() { 2 List<Runnable> tasks; 3 final ReentrantLock mainLock = this.mainLock; 4 mainLock.lock(); 5 try { 6 checkShutdownAccess(); 7 advanceRunState(STOP); 8 interruptWorkers(); 9 tasks = drainQueue(); 10 } finally { 11 mainLock.unlock(); 12 } 13 tryTerminate(); 14 return tasks; 15 }
對比過後,兩個方法,設定的執行緒池狀態不同,分別為 SHUTDOWN 和 STOP,中斷執行緒的細節不同,分別是中斷 idle 工作執行緒和中斷所有工作執行緒。
怎麼判斷工作執行緒是否空閒呢?原來工作執行緒執行任務的時候,會加鎖,任務執行完成後釋放鎖。
1 final void runWorker(Worker w) { 2 Thread wt = Thread.currentThread(); 3 Runnable task = w.firstTask; 4 w.firstTask = null; 5 w.unlock(); // allow interrupts 6 boolean completedAbruptly = true; 7 try { 8 while (task != null || (task = getTask()) != null) { 9 w.lock(); 10 // If pool is stopping, ensure thread is interrupted; 11 // if not, ensure thread is not interrupted. This 12 // requires a recheck in second case to deal with 13 // shutdownNow race while clearing interrupt 14 if ((runStateAtLeast(ctl.get(), STOP) || 15 (Thread.interrupted() && 16 runStateAtLeast(ctl.get(), STOP))) && 17 !wt.isInterrupted()) 18 wt.interrupt(); 19 try { 20 beforeExecute(wt, task); 21 try { 22 task.run(); 23 afterExecute(task, null); 24 } catch (Throwable ex) { 25 afterExecute(task, ex); 26 throw ex; 27 } 28 } finally { 29 task = null; 30 w.completedTasks++; 31 w.unlock(); 32 } 33 } 34 completedAbruptly = false; 35 } finally { 36 processWorkerExit(w, completedAbruptly); 37 } 38 }
中斷 idle 執行緒時,嘗試獲取鎖,獲取鎖成功則認定執行緒 idle
1 private void interruptIdleWorkers(boolean onlyOne) { 2 final ReentrantLock mainLock = this.mainLock; 3 mainLock.lock(); 4 try { 5 for (Worker w : workers) { 6 Thread t = w.thread; 7 if (!t.isInterrupted() && w.tryLock()) { 8 try { 9 t.interrupt(); 10 } catch (SecurityException ignore) { 11 } finally { 12 w.unlock(); 13 } 14 } 15 if (onlyOne) 16 break; 17 } 18 } finally { 19 mainLock.unlock(); 20 } 21 }
工作執行緒 idle,它必然阻塞在 getTask 處,它被中斷後,會根據執行緒池狀態返回 null task,完成工作執行緒的退出。
1 final void runWorker(Worker w) { 2 Thread wt = Thread.currentThread(); 3 Runnable task = w.firstTask; 4 w.firstTask = null; 5 w.unlock(); // allow interrupts 6 boolean completedAbruptly = true; 7 try { 8 while (task != null || (task = getTask()) != null) { 9 w.lock(); 10 // If pool is stopping, ensure thread is interrupted; 11 // if not, ensure thread is not interrupted. This 12 // requires a recheck in second case to deal with 13 // shutdownNow race while clearing interrupt 14 if ((runStateAtLeast(ctl.get(), STOP) || 15 (Thread.interrupted() && 16 runStateAtLeast(ctl.get(), STOP))) && 17 !wt.isInterrupted()) 18 wt.interrupt(); 19 try { 20 beforeExecute(wt, task); 21 try { 22 task.run(); 23 afterExecute(task, null); 24 } catch (Throwable ex) { 25 afterExecute(task, ex); 26 throw ex; 27 } 28 } finally { 29 task = null; 30 w.completedTasks++; 31 w.unlock(); 32 } 33 } 34 completedAbruptly = false; 35 } finally { 36 processWorkerExit(w, completedAbruptly); 37 } 38 }
總結:
shutdown 無法提交新任務,中斷空閒工作執行緒,等待佇列中的任務執行完畢。
shutdownNow 無法提交新任務,中斷所有執行緒,返回佇列中的任務。
可以看出,shutdown 方法已經足夠優雅了,那麼還有哪些地方需要增強呢?如果執行緒池佇列中堆積的任務太多,導致執行緒池關閉的時間太長,可以手動地將佇列中的任務全部移除(當然前提是可以獲取到佇列的引用),然後再關閉執行緒池;同時還可以結合 awaitTermination 方法等待執行緒池關閉。
那什麼時候可以使用 shutdownNow 呢,如果要快速關閉執行緒池,用這個方法似乎更優,但是它會中斷工作執行緒,而我們的業務程式碼不確定哪個地方會阻塞(RPC 呼叫,加鎖等待,讀取資料庫),很多阻塞程式碼可能會響應中斷,丟擲異常,導致任務執行異常,所以慎用。