線程池源碼問題的個人理解補充

今天看了部分線程池的代碼,對于線程池運行機制又有了一些新的認識, 本文是結合占小狼簡書 加了一些自己的理解和注釋.

該文只是作為個人對于占小狼關于深入分析java線程池的實現(xiàn)原理文的自己一些補充,建議先看他的文章之后再來看該文。有理解不對的地方望提出來共同學習。

1.線程池內部狀態(tài):
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // 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;

    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

AtomicInteger變量ctl的功能非常強大:利用低29位表示線程池中線程數(shù),通過高3位表示線程池的運行狀態(tài):
1、RUNNING:-1 << COUNT_BITS,即高3位為111,該狀態(tài)的線程池會接收新任務,并處理阻塞隊列中的任務;
2、SHUTDOWN: 0 << COUNT_BITS,即高3位為000,該狀態(tài)的線程池不會接收新任務,但會處理阻塞隊列中的任務;
3、STOP : 1 << COUNT_BITS,即高3位為001,該狀態(tài)的線程不會接收新任務,也不會處理阻塞隊列中的任務,而且會中斷正在運行的任務;
4、TIDYING : 2 << COUNT_BITS,即高3位為010;
5、TERMINATED: 3 << COUNT_BITS,即高3位為011;

2.任務執(zhí)行
//得到c從而拿到內部狀態(tài)
 int c = ctl.get();
        //拿到線程池中的線程數(shù),如果線程數(shù)小于核心線程數(shù),那么直接創(chuàng)建新的線程執(zhí)行任務
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
           //會發(fā)現(xiàn)線程池中很多地方都會重復的得到內部狀態(tài),因為線程池主要是用CAS來保證的線程安全性,從而導致如果swap失敗的一些情況需要再次獲得最新的內部狀態(tài)用之后做判斷。
           //這里如果加入創(chuàng)建新的線程執(zhí)行任務的時候出現(xiàn)錯誤,重新獲取最新的內部狀態(tài)用于之后使用
            c = ctl.get();
        }
        //workQueue.offer會跟根據(jù)不同的阻塞隊列從而導致線程池出現(xiàn)不同的線程添加機制,我會在2.1中著重說不同阻塞隊列的情況。
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
           //如果在我們添加到阻塞隊列之后,狀態(tài)不是RUNNING狀態(tài),會將當前任務從阻塞隊列移除,并拒絕這次任務
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //這種情況是由于corePoolSize允許為0,當corePoolSize為0時,第一次會運行到這步,并添加線程到線程池中。當corePoolSize等于0時,會相當于只在核心線程池中添加一個線程用于消費阻塞隊列的任務,這里也會在2.1結合不同阻塞隊列說
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
2.1 不同阻塞隊列對于線程池添加的影響。(這里建議先看過上面推薦的博客,并了解了核心線程池中的workers是什么以及線程池的流程)
Paste_Image.png

workers其實就是存儲這里的MaxPool中包含的所有的工作線程的一個HashSet

workQueue.offer其實就是要將任務添加到阻塞隊列中,如果能夠添加則返回true并添加到阻塞隊列中,否則返回false。
addWorker這個步驟其實就是要向workers中創(chuàng)建一個新的線程用于消費阻塞隊列中的任務。
但是對于不同的阻塞隊列,offer的行為是不同的。
LinkedBlockingQueue,當我們初始化的時候沒有給他初始容量,那么這里,他每次offer都可以添加到我們的阻塞隊列中,因為LinkedBlockingQueue是基于鏈表結構的無界阻塞隊列。那么我們如果corePoolSize不是0,則相當于只有當前workers中只有CorePool,當workerCountOf(c) > corePoolSize的時候,我們只是向阻塞隊列中添加任務,供之后線程消費,而不會再添加新的worker到workers了,所以這個時候的MaxPool和CorePool是一樣大的,maxmumPoolSize參數(shù)也就沒有了意義。如果corePoolSize是0,則相當于只有一個線程在線程池中,之后的任務都直接進入到阻塞隊列
LinkedBlockingQueue賦予了初始化容量,那么我的理解是和ArrayBlockingQueue作用是一樣的。當我們的數(shù)量達到了核心線程數(shù),接下來會向阻塞隊列中添加任務,當我們的阻塞隊列也滿了。則再創(chuàng)建新的worker加入到workers中,當達到最大線程數(shù)時,最后會reject。
當我們當前的線程池核心線程數(shù)大小小于corePoolSize的時候,每次都會創(chuàng)建新的woker來執(zhí)行,當我們等于核心線程數(shù)的時候,如果這個時候存在空閑的worker,那么會直接使用空閑的worker執(zhí)行,當沒有空閑worker的時候會向阻塞隊列中添加command
SynchronousQueue,擴展先閱讀下http://ifeve.com/java-synchronousqueue/.SynchronouseQueue主要是用來做信號量方面的應用的,Excutors.newCachedThreadPool就是用的這種方式從而達到緩存線程池的作用。
這里的SynchronouseQueue用的很精妙.
offer不是每回都返回false,而是當我們當前的可用的worker隊列中,如果不存在可用的worker的時候才會返回false,如果存在可用的worker的時候,會返回 true,這是因為當我們worker執(zhí)行完當前work的任務的時候,我們回去在繼續(xù)getTask,發(fā)現(xiàn)task不存在的時候,我們會去調用workQueue.take或poll,從而阻塞在這里,當我們有可用woker的時候,相當于阻塞隊列存在一個待消費的take,這個時候offer會返回true

因為之前一直對不同隊列對線程池運行有什么影響不是很理解,這一部分是我經(jīng)過測試并看了一部分源碼得出的一些理解,有問題希望大家能提出來。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容