摘要:有細心的網友早就想到了這個問題在線程池中,還有一些不常用的設置。所以該方法會在線程池總預先創建沒有任務執行的線程,數量為。下面我們測試一下從測試結果來看,線程池中已經預先創建了數量的空閑線程。
微信公眾號「后端進階」,專注后端技術分享:Java、Golang、WEB框架、分布式中間件、服務治理等等。
老司機傾囊相授,帶你一路進階,來不及解釋了快上車!
看完我上一篇文章「你都理解創建線程池的參數嗎?」之后,當遇到這種問題,你覺得你完全能夠唬住面試官了,50k輕松到手。殊不知,要是面試官此刻給你來個反殺:
初始化線程池時可以預先創建線程嗎?線程池的核心線程可以被回收嗎?為什么?
如果此刻你一臉懵逼,這個要慌,問題很大,50k馬上變5k。
有細心的網友早就想到了這個問題:
在ThreadPoolExecutor線程池中,還有一些不常用的設置。我建議如果您在應用場景中沒有特殊的要求,就不需要使用這些設置。
初始化線程池時可以預先創建線程嗎? prestartAllCoreThreads初始化線程池時是可以預先創建線程的,初始化線程池后,再調用prestartAllCoreThreads()方法,即可預先創建corePoolSize數量的核心線程,我們看源碼:
public int prestartAllCoreThreads() { int n = 0; while (addWorker(null, true)) ++n; return n; }
private boolean addWorker(Runnable firstTask, boolean core) { // .. }
addWorker方法目的是在線程池中添加任務并執行,如果task為空,線程獲取任務執行時調用getTask()方法,該方法從blockingQueue阻塞隊列中阻塞獲取任務執行,因此線程不會釋放,留存在線程池中,如果core=true,說明任務只能利用核心線程來執行。
所以該方法會在線程池總預先創建沒有任務執行的線程,數量為corePoolSize。
下面我們測試一下:
從測試結果來看,線程池中已經預先創建了corePoolSize數量的空閑線程。
prestartCoreThreadprestartCoreThread()同樣可以預先創建線程,只不過該方法只會與創建1條線程,我們來看源碼:
public boolean prestartCoreThread() { return workerCountOf(ctl.get()) < corePoolSize && addWorker(null, true); }
從方法源碼可知,如果此時工作線程數量小于corePoolSize,那么就調用addWorker創建1條空閑核心線程。
下面我們測試一下:
從測試結果來看,線程池中已經預先創建了1條空閑線程。
線程池的核心線程可以被回收嗎?你可能會想到將corePoolSize的數量設置為0,從而線程池的所有線程都是“臨時”的,只有keepAliveTime存活時間,你的思路也許時正確的,但你有沒有想過一個很嚴重的后果,corePoolSize=0時,任務需要填滿阻塞隊列才會創建線程來執行任務,阻塞隊列有設置長度還好,如果隊列長度無限大呢,你就等著OOM異常吧,所以用這種設置行為并不是我們所需要的。
有沒有什么設置可以回收核心線程呢?
allowCoreThreadTimeOutThreadPoolExecutor有一個私有成員變量:
private volatile boolean allowCoreThreadTimeOut;
如果allowCoreThreadTimeOut=true,核心線程在規定時間內會被回收。
上面我也說了,當線程空閑時會從blockingQueue阻塞隊列中阻塞獲取任務執行,所以我們來看看是保證核心線程不被銷毀的,我們直接定位到源碼部位:
java.util.concurrent.ThreadPoolExecutor#getTask:
boolean timedOut = false; // Did the last poll() time out? for (;;) { // Are workers subject to culling? boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; try { Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } }
這里的關鍵值timed,如果allowCoreThreadTimeOut=true或者此時工作線程大于corePoolSize,timed=true,如果timed=true,會調用poll()方法從阻塞隊列中獲取任務,否則調用take()方法獲取任務。
下面我來解釋這兩個方法:
poll(long timeout, TimeUnit unit):從BlockingQueue取出一個任務,如果不能立即取出,則可以等待timeout參數的時間,如果超過這個時間還不能取出任務,則返回null;
take():從blocking阻塞隊列取出一個任務,如果BlockingQueue為空,阻斷進入等待狀態直到BlockingQueue有新的任務被加入為止。
到這里,我們就很好地解釋了,當allowCoreThreadTimeOut=true或者此時工作線程大于corePoolSize時,線程調用BlockingQueue的poll方法獲取任務,若超過keepAliveTime時間,則返回null,timedOut=true,則getTask會返回null,線程中的runWorker方法會退出while循環,線程接下來會被回收。
下面我們測試一下:
可以看到,核心線程被回收了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77615.html
摘要:所以說我們的線程最好是交由線程池來管理,這樣可以減少對線程生命周期的管理,一定程度上提高性能。線程池不接收新任務,不處理已添加的任務,并且會中斷正在處理的任務。當所有的任務已終止,記錄的任務數量為,線程池會變為狀態。線程池徹底終止的狀態。 前言 只有光頭才能變強 回顧前面: ThreadLocal就是這么簡單 多線程三分鐘就可以入個門了! 多線程基礎必要知識點!看了學習多線程事半功倍...
摘要:此時線程需要鎖才能繼續往下執行。但是線程的鎖并沒有釋放,線程的鎖也沒有釋放。 前言 只有光頭才能變強 回顧前面: ThreadLocal就是這么簡單 多線程三分鐘就可以入個門了! 多線程基礎必要知識點!看了學習多線程事半功倍 Java鎖機制了解一下 AQS簡簡單單過一遍 Lock鎖子類了解一下 線程池你真不來了解一下嗎? 本篇主要是講解死鎖,這是我在多線程的最后一篇了。主要將多線程...
摘要:的方法,的默認實現會判斷是否是類型注意自動拆箱,自動裝箱問題。適應自旋鎖鎖競爭是下的,會經過用戶態到內核態的切換,是比較花時間的。在中引入了自適應的自旋鎖,說明自旋的時間不固定,要不要自旋變得越來越聰明。 前言 只有光頭才能變強 之前在刷博客的時候,發現一些寫得比較好的博客都會默默收藏起來。最近在查閱補漏,有的知識點比較重要的,但是在之前的博客中還沒有寫到,于是趁著閑整理一下。 文本的...
摘要:前言本文是一篇簡短的雜糅本文源自于作者最近的一個疑問為什么在舊版的中偏向鎖的移除一定要在全局安全點進行同時在上個星期作者參與的一個項目發生了一件怪事一個服務莫名其妙地不接受任何請求了一切請求都是而查看日志發現出故障的服務本身去請求另一個服務 前言 本文是一篇簡短的雜糅. 本文源自于作者最近的一個疑問:為什么在舊版的jdk中偏向鎖的移除一定要在全局安全點進行?同時在上個星期,作者參與的一...
摘要:受知乎文章和設計模式之禪的啟發,我也來搞一篇腦洞小開的文章由標題可知,這篇文章是寫給我女朋友看的。于是這就讓經紀人對粉絲說只有萬,我才會寫代碼。 前言 只有光頭才能變強 回顧前面: ThreadLocal就是這么簡單 多線程三分鐘就可以入個門了! 多線程基礎必要知識點!看了學習多線程事半功倍 Java鎖機制了解一下 AQS簡簡單單過一遍 Lock鎖子類了解一下 線程池你真不來了解一下...
閱讀 1829·2021-09-14 18:03
閱讀 2267·2019-08-30 15:48
閱讀 1121·2019-08-30 14:09
閱讀 507·2019-08-30 12:55
閱讀 2724·2019-08-29 11:29
閱讀 1483·2019-08-26 13:43
閱讀 2311·2019-08-26 13:30
閱讀 2369·2019-08-26 12:17