閉線程池)
為什么關(guān)閉線程池線程池關(guān)閉的意義不僅僅在于結(jié)束線程執(zhí)行避免內(nèi)存溢出因為大多使用的場景并非上述示例那樣朝生夕死。線程池一般是持續(xù)工作的全局場景如數(shù)據(jù)庫連接池。線程池大家都知道用來實現(xiàn)資源復(fù)用的。避免了線程頻繁的創(chuàng)建和銷毀的開銷。待到使用完畢后都要關(guān)閉線程池以釋放資源避免線程全部泄漏白白的浪費了內(nèi)存。容易導(dǎo)致內(nèi)存吃緊的時候造成死機。線程池有運行任務(wù)自然也有關(guān)閉任務(wù)線程池手動關(guān)閉無非是兩個方法其實無非就是兩個方法 shutdown()/shutdownNow()。自動關(guān)閉則需要注意線程池配置技巧。線程池手動關(guān)閉關(guān)閉方法線程池有兩個方法 shutdown()/shutdownNow()用來關(guān)閉二者有著重要的區(qū)別shutdown() 執(zhí)行后停止接受新任務(wù)會把隊列的任務(wù)執(zhí)行完畢。shutdownNow() 也是停止接受新任務(wù)但會中斷所有的任務(wù)將線程池狀態(tài)變?yōu)?stop。下段源碼是shutdown() 和shutdownNow()從中可以看到兩個方法的不同/** * Initiates an orderly shutdown in which previously submitted * tasks are executed, but no new tasks will be accepted. * Invocation has no additional effect if already shut down. * * pThis method does not wait for previously submitted tasks to * complete execution. Use {link #awaitTermination awaitTermination} * to do that. * * throws SecurityException {inheritDoc} */ public void shutdown() { final ReentrantLock mainLock this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(SHUTDOWN); interruptIdleWorkers(); onShutdown(); // hook for ScheduledThreadPoolExecutor } finally { mainLock.unlock(); } tryTerminate(); } /** * Attempts to stop all actively executing tasks, halts the * processing of waiting tasks, and returns a list of the tasks * that were awaiting execution. These tasks are drained (removed) * from the task queue upon return from this method. * * pThis method does not wait for actively executing tasks to * terminate. Use {link #awaitTermination awaitTermination} to * do that. * * pThere are no guarantees beyond best-effort attempts to stop * processing actively executing tasks. This implementation * cancels tasks via {link Thread#interrupt}, so any task that * fails to respond to interrupts may never terminate. * * throws SecurityException {inheritDoc} */ public ListRunnable shutdownNow() { ListRunnable tasks; final ReentrantLock mainLock this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(STOP); interruptWorkers(); tasks drainQueue(); } finally { mainLock.unlock(); } tryTerminate(); return tasks; }shutdown()將線程池的狀態(tài)修改為SHUTDOWN狀態(tài)然后調(diào)用interruptIdleWorkers方法來中斷空閑的線程。方法是安全的關(guān)閉線程池調(diào)用shutdown方法后不是立即關(guān)閉線程池而是在線程池中執(zhí)行很多任務(wù)或者等待隊列中執(zhí)行任務(wù)等待所有任務(wù)完成后關(guān)閉線程池shutdownNow() 方法的執(zhí)行邏輯將線程池狀態(tài)修改為STOP然后調(diào)用線程池里的所有線程的interrupt方法。shutdownNow()立即關(guān)閉線程池首先向線程池中的線程發(fā)送中斷信號嘗試中斷線程然后將等待隊列的任務(wù)返回調(diào)用人員讓調(diào)用人員補救這些任務(wù)。通??梢愿鶕?jù)自己的業(yè)務(wù)需求選擇合適的方法停止線程池。例如通??梢杂胹hutdown()的方法關(guān)閉完成提交的任務(wù)但如果情況緊急可以用shutdownnow方法加快線程池的結(jié)束速度。我通常是按照以下方式關(guān)閉線程池的/** * author Evan Walker * version 1.0 * desc 線程池關(guān)閉demo */ public class ThreadPoolExecutorDemo { private final static Logger logger LoggerFactory.getLogger(ThreadPoolExecutorDemo.class); //起的線程數(shù) private static final Integer THREAD_NUM 5; //創(chuàng)建線程池 ThreadPoolExecutor pool new ThreadPoolExecutor(THREAD_NUM, THREAD_NUM * 2, 0, TimeUnit.SECONDS, new LinkedBlockingQueue(100)); public void ThreadPoolExecutorClose(Runnable command){ long start System.currentTimeMillis(); for (int i0; i 5; i) { pool.execute(command); } pool.shutdown(); try { while (!pool.awaitTermination(1, TimeUnit.SECONDS)) { logger.info(線程還在執(zhí)行。。。); } }catch (InterruptedException e){ logger.error(ThreadPoolExecutor InterruptedException ,e); } long end System.currentTimeMillis(); logger.info(一共處理了【{}】, (end - start)); } }也可以設(shè)置一個shutdownHook來關(guān)閉線程池Runtime.getRuntime().addShutdownHook(new Thread(() - { try { pool.shutdown(); while(!pool.awaitTermination(10,TimeUnit.SECONDS)) {} } catch(Exception e) { logger.error(pool shutdown has exception ,e); } }));pool.awaitTermination(1, TimeUnit.SECONDS) 會每隔一秒鐘檢查一次是否執(zhí)行完畢狀態(tài)為 TERMINATED當(dāng)從 while 循環(huán)退出時就表明線程池已經(jīng)完全終止了。關(guān)閉狀態(tài)判斷1 isShutdownboolean isShutdown()此方法在線程已經(jīng)開始關(guān)閉的時候返回true其他時候返回false即是否調(diào)用了線程池的shutdown或shutdownnow方法返回true不代表線程池已經(jīng)終止了還有可能是需要線程池需要等待正在執(zhí)行的任務(wù)或任務(wù)隊列中的任務(wù)執(zhí)行完。2 isTerminated此方法返回true表示線程池的所有任務(wù)都執(zhí)行完畢了線程池終結(jié)了如果線程池調(diào)用了shutdown但是任務(wù)還沒執(zhí)行完isShutdown返回true而isTerminated返回false。3.awaittermination方法是判斷線程池是否完全關(guān)閉與isterminated相似但接受等待時間。調(diào)用該方法可能發(fā)生以下情況1等待期間(包括進入等待狀態(tài))線程池關(guān)閉提交的任務(wù)(包括執(zhí)行中和隊列中等待的)全部完成相當(dāng)于線程池結(jié)束方法返回true2等待超時后最初的線程池沒有發(fā)生結(jié)束法回到false3等待期間線程中斷方法會拋出互聯(lián)網(wǎng)異常。線程池自動關(guān)閉ThreadPoolExecutor類源碼中有這樣一句話A pool that is no longer referenced in a program and has no remaining threads will be shutdown automatically.沒有引用指向且沒有剩余線程的線程池將會自動關(guān)閉。先來看一下ThreadPoolExecutor參數(shù)最全的構(gòu)造方法/** * param corePoolSize the number of threads to keep in the pool, even * if they are idle, unless {code allowCoreThreadTimeOut} is set * 核心線程數(shù)即使是空閑狀態(tài)也可以在線程池存活的線程數(shù)量除非 * allowCoreThreadTimeOut 設(shè)置為 true。 * param keepAliveTime when the number of threads is greater than * the core, this is the maximum time that excess idle threads * will wait for new tasks before terminating. * 存活時間對于超出核心線程數(shù)的線程空閑時間一旦達到存活時間就會被銷毀。 */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueRunnable workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { ... ... }方案一自動關(guān)閉線程池核心線程數(shù)為 0 并指定線程存活時間public class ThreadPoolTest { public static void main(String[] args) { // 重點關(guān)注 corePoolSize 和 keepAliveTime其他參數(shù)不重要 ThreadPoolExecutor executor new ThreadPoolExecutor(0, 5, 15L, TimeUnit.SECONDS, new LinkedBlockingQueue(15)); for (int i 0; i 20; i) { executor.execute(() - { // 簡單地打印當(dāng)前線程名稱 System.out.println(Thread.currentThread().getName()); }); } } }通過執(zhí)行打印可以發(fā)現(xiàn)線程池在15秒之后自動關(guān)閉因為它核心線程核心線程數(shù)為 0 并指定線程存活時間且后續(xù)沒有繼續(xù)使用到該線程池在其最后一個線程執(zhí)行完畢后15秒之后會優(yōu)雅的關(guān)閉線程池。而如果將corePoolSize設(shè)置為大于0的數(shù)字再運行以上代碼那么線程池將一直處于等待狀態(tài)而不能關(guān)閉因為核心線程不受keepAliveTime控制所以會一直存活程序也將一直不能結(jié)束。運行效果如下 (corePoolSize設(shè)置為5其他參數(shù)不變)線程池工作時當(dāng)有新的任務(wù)到來時程序會先判斷線程池當(dāng)前線程數(shù)是否達到corePoolSize(核心線程數(shù))沒達到則創(chuàng)建線程執(zhí)行任務(wù)達到則嘗試將任務(wù)放入任務(wù)隊列 (workQueue)。如果將corePoolSize設(shè)置為0的話新到來的任務(wù)會永遠優(yōu)先被放入任務(wù)隊列然后等待被處理這顯然會影響程序的執(zhí)行效率。所以就有了下面的解決方案。方案二自動關(guān)閉線程池通過 allowCoreThreadTimeOut 控制核心線程存活時間從JDK 1.6開始ThreadPoolExecutor類新增了一個allowCoreThreadTimeOut字段/** * If false (default), core threads stay alive even when idle. * If true, core threads use keepAliveTime to time out waiting * for work. * 默認為false核心線程處于空閑狀態(tài)也可一直存活 * 如果設(shè)置為true核心線程的存活狀態(tài)將受keepAliveTime控制超時將被銷毀 */ private volatile boolean allowCoreThreadTimeOut;這個字段值默認為false可使用allowCoreThreadTimeOut()方法對其進行設(shè)置如果設(shè)置為true那么核心線程數(shù)也將受keepAliveTime控制此方法源碼如下public void allowCoreThreadTimeOut(boolean value) { // 核心線程存活時間必須大于0一旦開啟keepAliveTime 也必須大于0 if (value keepAliveTime 0) throw new IllegalArgumentException(Core threads must have nonzero keep alive times); // 將 allowCoreThreadTimeOut 值設(shè)為傳入的參數(shù)值 if (value ! allowCoreThreadTimeOut) { allowCoreThreadTimeOut value; // 開啟后清理所有的超時空閑線程包括核心線程 if (value) interruptIdleWorkers(); } }public class ThreadPoolTest { public static void main(String[] args) { // 這里把corePoolSize設(shè)為5keepAliveTime保持不變 ThreadPoolExecutor executor new ThreadPoolExecutor(5, 5, 15L, TimeUnit.SECONDS, new LinkedBlockingQueue(15)); // 允許核心線程超時銷毀 executor.allowCoreThreadTimeOut(true); for (int i 0; i 20; i) { executor.execute(() - { System.out.println(Thread.currentThread().getName()); }); } } }運行結(jié)果可以看到程序在打印結(jié)束后等待了15s然后自行退出說明線程池已自動關(guān)閉也就是allowCoreThreadTimeOut()方法發(fā)揮了作用。這樣我們就實現(xiàn)了可自動關(guān)閉且核心線程數(shù)不為0的線程池。方案三自動關(guān)閉線程池線程池中的線程設(shè)置為守護線程public class ThreadPoolTest { public static void main(String[] args) { ThreadPoolExecutor executor new ThreadPoolExecutor(3, 5, 15L, TimeUnit.SECONDS, new LinkedBlockingQueue(15),new ThreadFactory() { public Thread newThread(Runnable r) { Thread thread new Thread(r, r.hashCode()); thread.setDaemon(true);//設(shè)置成守護線程 return thread; } }); for (int i 0; i 20; i) { executor.execute(() - { // 簡單地打印當(dāng)前線程名稱 System.out.println(Thread.currentThread().getName()); }); } } }雖然corePoolSize0而且沒有設(shè)置allowCoreThreadTimeOut但是在創(chuàng)建線程池時通過ThreadFactory指定了線程為守護線程。當(dāng)線程打印結(jié)束后無需等待程序正常退出說明線程池關(guān)閉了。PS守護進程Daemon是運行在后臺的一種特殊進程。它獨立于控制終端并且周期性地執(zhí)行某種任務(wù)或等待處理某些發(fā)生的事件。也就是說守護線程不依賴于終端但是依賴于系統(tǒng)與系統(tǒng)“同生共死”。文章參考https://segmentfault.com/a/1190000021225019java線程池的關(guān)閉_線程池 非核心線程關(guān)閉的配置-CSDN博客