ThreadPoolExecutor源碼分析(一):重要的成員變量

ThreadPoolExecutor部分重要成員變量:
1、AtomicInteger ctl
2宇整、workQueue
3瓶佳、corePoolSize
4、maximumPoolSize
5鳞青、keepAliveTime
6霸饲、handler

一为朋、AtomicInteger ctl

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代表了ThreadPoolExecutor中的控制狀態(tài),它是一個復(fù)核類型的成員變量厚脉,是一個原子整數(shù)习寸,借助高低位包裝了兩個概念:

  1. workerCount:線程池中當(dāng)前活動的線程數(shù)量,占據(jù)ctl的低29位傻工;
  2. runState:線程池運(yùn)行狀態(tài)霞溪,占據(jù)ctl的高3位,有RUNNING中捆、SHUTDOWN鸯匹、STOP、TIDYING泄伪、TERMINATED五種狀態(tài)殴蓬。

workerCount:

COUNT_BITS的定義代表了workerCount所占位數(shù):

private static final int COUNT_BITS = Integer.SIZE - 3;
//在Java中,一個int占據(jù)32位蟋滴,而COUNT_BITS的結(jié)果不言而喻染厅,Integer大小32減去3,就是29津函。

CAPACITY的定義限制了workerCount的理論最大活躍線程數(shù):

private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
//運(yùn)算過程為1左移29位肖粮,也就是00000000 00000000 00000000 00000001 --> 001 0000 00000000 00000000 00000000,
//再減去1球散,就是 000 11111 11111111 11111111 11111111尿赚,
//前三位代表線程池運(yùn)行狀態(tài)runState,
//所以這里workerCount的理論最大值就應(yīng)該是29個1蕉堰,即536870911。

workerCount作為其中一個概念復(fù)合在AtomicInteger ctl中悲龟,ThreadPoolExecutor也提供了從AtomicInteger ctl中解析出workerCount的方法屋讶,如下:

private static int workerCountOf(int c)  { 
    return c & CAPACITY; 
}
//傳入的c代表的是ctl的值,即高3位為線程池運(yùn)行狀態(tài)runState须教,低29位為線程池中當(dāng)前活動的線程數(shù)量workerCount.
//將其與CAPACITY進(jìn)行與操作&皿渗,也就是與000 11111 11111111 11111111 11111111進(jìn)行與操作,
//c的前三位通過與000進(jìn)行與操作轻腺,無論c前三位為何值乐疆,最終都會變成000,也就是舍棄前三位的值贬养,
//而c的低29位與29個1進(jìn)行與操作挤土,c的低29位還是會保持原值,
//這樣就從AtomicInteger ctl中解析出了workerCount的值误算。

runState:

線程池運(yùn)行狀態(tài)仰美,它占據(jù)ctl的高3位迷殿,有RUNNING、SHUTDOWN咖杂、STOP庆寺、TIDYING、TERMINATED五種狀態(tài)诉字。我們先分別解釋下這五種狀態(tài):

  1. RUNNING:接受新任務(wù)懦尝,并處理隊(duì)列任務(wù)
//Accept new tasks and process queued tasks
private static final int RUNNING    = -1 << COUNT_BITS;

-1在Java底層是由32個1表示的,左移29位的話壤圃,即111 00000 00000000 00000000 00000000导披,也就是低29位全部為0,高3位全部為1的話埃唯,表示RUNNING狀態(tài)撩匕,即-536870912;

  1. SHUTDOWN:不接受新任務(wù)墨叛,但會處理隊(duì)列任務(wù)
//Don't accept new tasks, but process queued tasks
private static final int SHUTDOWN   =  0 << COUNT_BITS;

0在Java底層是由32個0表示的止毕,無論左移多少位,還是32個0漠趁,即000 00000 00000000 00000000 00000000扁凛,也就是低29位全部為0,高3位全部為0的話闯传,表示SHUTDOWN狀態(tài)谨朝,即0。

  1. STOP:不接受新任務(wù)甥绿,不會處理隊(duì)列任務(wù)字币,而且會中斷正在處理過程中的任務(wù)
//Don't accept new tasks, don't process queued tasks,
//and interrupt in-progress tasks
private static final int STOP       =  1 << COUNT_BITS;

1在Java底層是由前面的31個0和1個1組成的,左移29位的話共缕,即001 00000 00000000 00000000 00000000洗出,也就是低29位全部為0,高3位為001的話图谷,表示STOP狀態(tài)翩活,即536870912;

  1. TIDYING:所有的任務(wù)已結(jié)束便贵,workerCount為0彬坏,線程過渡到TIDYING狀態(tài)鲁豪,將會執(zhí)行terminated()鉤子方法。
/*
All tasks have terminated, workerCount 
is zero,the thread transitioning to 
state TIDYING will run the terminated() hook method
*/
private static final int TIDYING    =  2 << COUNT_BITS;

2在Java底層是由前面的30個0和1個10組成的,左移29位的話箩退,即010 00000 00000000 00000000 00000000,也就是低29位全部為0,高3位為010的話,表示TIDYING狀態(tài)魂毁,即1073741824。

  1. TERMINATED:terminated()方法已經(jīng)完成
//terminated() has completed
private static final int TERMINATED =  3 << COUNT_BITS;

3在Java底層是由前面的30個0和1個11組成的出嘹,左移29位的話席楚,即011 00000 00000000 00000000 00000000,也就是低29位全部為0税稼,高3位為011的話烦秩,表示TERMINATED狀態(tài),即1610612736郎仆。

由上面我們可以得知只祠,運(yùn)行狀態(tài)的值按照RUNNING-->SHUTDOWN-->STOP-->TIDYING-->TERMINATED順序值是遞增的,這些值之間的數(shù)值順序很重要扰肌。隨著時間的推移抛寝,運(yùn)行狀態(tài)單調(diào)增加,但是不需要經(jīng)過每個狀態(tài)曙旭。那么盗舰,可能存在的線程池狀態(tài)的轉(zhuǎn)換是什么呢?如下:

     * RUNNING -> SHUTDOWN
     *    On invocation of shutdown(), perhaps implicitly in finalize()
     * (RUNNING or SHUTDOWN) -> STOP
     *    On invocation of shutdownNow()
     * SHUTDOWN -> TIDYING
     *    When both queue and pool are empty
     * STOP -> TIDYING
     *    When pool is empty
     * TIDYING -> TERMINATED
     *    When the terminated() hook method has completed
     *
  1. RUNNING -> SHUTDOWN:調(diào)用shutdown()方法后桂躏,或者線程池實(shí)現(xiàn)了finalize方法钻趋,在里面調(diào)用了shutdown方法,即隱式調(diào)用剂习。
  2. (RUNNING or SHUTDOWN) -> STOP:調(diào)用shutdownNow()方法后蛮位。
  3. SHUTDOWN -> TIDYING:線程池和隊(duì)列均為空時。
  4. STOP -> TIDYING:線程池為空時鳞绕。
  5. TIDYING -> TERMINATED:terminated()鉤子方法完成時失仁。

接下來看看如何從ctl中提取runState:

private static int runStateOf(int c)     { return c & ~CAPACITY; }

~ 是按位取反的意思,CAPACITY表示的是高位的3個0猾昆,和低位的29個1陶因,而~CAPACITY則表示高位的3個1,2低位的9個0垂蜗,然后再與入?yún)執(zhí)行按位與操作,即高3位保持原樣解幽,低29位全部設(shè)置為0贴见,也就獲取了線程池的運(yùn)行狀態(tài)runState。

最后看一下這個方法:

private static int ctlOf(int rs, int wc) { return rs | wc; }

將runState和workerCount做或操作|處理躲株,即用runState的高3位片部,workerCount的低29位填充的數(shù)字,而默認(rèn)傳入的runState霜定、workerCount分別為RUNNING和0档悠。

接下來看一下其他幾個重要的成員變量:

    /**
     * The queue used for holding tasks and handing off to worker
     * threads.  We do not require that workQueue.poll() returning
     * null necessarily means that workQueue.isEmpty(), so rely
     * solely on isEmpty to see if the queue is empty (which we must
     * do for example when deciding whether to transition from
     * SHUTDOWN to TIDYING).  This accommodates special-purpose
     * queues such as DelayQueues for which poll() is allowed to
     * return null even if it may later return non-null when delays
     * expire.
     */
    private final BlockingQueue<Runnable> workQueue;

    /**
     * Lock held on access to workers set and related bookkeeping.
     * While we could use a concurrent set of some sort, it turns out
     * to be generally preferable to use a lock. Among the reasons is
     * that this serializes interruptIdleWorkers, which avoids
     * unnecessary interrupt storms, especially during shutdown.
     * Otherwise exiting threads would concurrently interrupt those
     * that have not yet interrupted. It also simplifies some of the
     * associated statistics bookkeeping of largestPoolSize etc. We
     * also hold mainLock on shutdown and shutdownNow, for the sake of
     * ensuring workers set is stable while separately checking
     * permission to interrupt and actually interrupting.
     */
    private final ReentrantLock mainLock = new ReentrantLock();

    /**
     * Set containing all worker threads in pool. Accessed only when
     * holding mainLock.
     */
    private final HashSet<Worker> workers = new HashSet<Worker>();

    /**
     * Wait condition to support awaitTermination
     */
    private final Condition termination = mainLock.newCondition();

    /**
     * Tracks largest attained pool size. Accessed only under
     * mainLock.
     */
    private int largestPoolSize;

    /**
     * Counter for completed tasks. Updated only on termination of
     * worker threads. Accessed only under mainLock.
     */
    private long completedTaskCount;

    /*
     * All user control parameters are declared as volatiles so that
     * ongoing actions are based on freshest values, but without need
     * for locking, since no internal invariants depend on them
     * changing synchronously with respect to other actions.
     */

    /**
     * Factory for new threads. All threads are created using this
     * factory (via method addWorker).  All callers must be prepared
     * for addWorker to fail, which may reflect a system or user's
     * policy limiting the number of threads.  Even though it is not
     * treated as an error, failure to create threads may result in
     * new tasks being rejected or existing ones remaining stuck in
     * the queue.
     *
     * We go further and preserve pool invariants even in the face of
     * errors such as OutOfMemoryError, that might be thrown while
     * trying to create threads.  Such errors are rather common due to
     * the need to allocate a native stack in Thread.start, and users
     * will want to perform clean pool shutdown to clean up.  There
     * will likely be enough memory available for the cleanup code to
     * complete without encountering yet another OutOfMemoryError.
     */
    private volatile ThreadFactory threadFactory;

    /**
     * Handler called when saturated or shutdown in execute.
     */
    private volatile RejectedExecutionHandler handler;

    /**
     * Timeout in nanoseconds for idle threads waiting for work.
     * Threads use this timeout when there are more than corePoolSize
     * present or if allowCoreThreadTimeOut. Otherwise they wait
     * forever for new work.
     */
    private volatile long keepAliveTime;

    /**
     * If false (default), core threads stay alive even when idle.
     * If true, core threads use keepAliveTime to time out waiting
     * for work.
     */
    private volatile boolean allowCoreThreadTimeOut;

    /**
     * Core pool size is the minimum number of workers to keep alive
     * (and not allow to time out etc) unless allowCoreThreadTimeOut
     * is set, in which case the minimum is zero.
     */
    private volatile int corePoolSize;

    /**
     * Maximum pool size. Note that the actual maximum is internally
     * bounded by CAPACITY.
     */
    private volatile int maximumPoolSize;

    /**
     * The default rejected execution handler
     */
    private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();

private final BlockingQueue<Runnable> workQueue;              //任務(wù)緩存隊(duì)列廊鸥,用來存放等待執(zhí)行的任務(wù)
private final ReentrantLock mainLock = new ReentrantLock();   //線程池的主要狀態(tài)鎖,對線程池狀態(tài)(比如線程池大小
                                                              //辖所、runState等)的改變都要使用這個鎖
private final HashSet<Worker> workers = new HashSet<Worker>();  //用來存放工作集
 
private volatile long  keepAliveTime;    //線程存貨時間   
private volatile boolean allowCoreThreadTimeOut;   //是否允許為核心線程設(shè)置存活時間
private volatile int   corePoolSize;     //核心池的大卸杷怠(即線程池中的線程數(shù)目大于這個參數(shù)時,提交的任務(wù)會被放進(jìn)任務(wù)緩存隊(duì)列)
private volatile int   maximumPoolSize;   //線程池最大能容忍的線程數(shù)
 
private volatile int   poolSize;       //線程池中當(dāng)前的線程數(shù)
 
private volatile RejectedExecutionHandler handler; //任務(wù)拒絕策略
 
private volatile ThreadFactory threadFactory;   //線程工廠缘回,用來創(chuàng)建線程
 
private int largestPoolSize;   //用來記錄線程池中曾經(jīng)出現(xiàn)過的最大線程數(shù)
 
private long completedTaskCount;   //用來記錄已經(jīng)執(zhí)行完畢的任務(wù)個數(shù)

二吆视、workQueue:

阻塞隊(duì)列,用來存儲等待執(zhí)行的任務(wù)酥宴,這里的阻塞隊(duì)列有以下幾種選擇:

ArrayBlockingQueue:基于數(shù)組的先進(jìn)先出隊(duì)列啦吧,此隊(duì)列創(chuàng)建時必須指定大小拙寡;
LinkedBlockingQueue:基于鏈表的先進(jìn)先出隊(duì)列授滓,如果創(chuàng)建時沒有指定此隊(duì)列大小,則默認(rèn)為Integer.MAX_VALUE肆糕;
synchronousQueue:這個隊(duì)列比較特殊般堆,它不會保存提交的任務(wù),而是將直接新建一個線程來執(zhí)行新來的任務(wù)擎宝。

三郁妈、corePoolSize:

核心池的大小,這個參數(shù)跟后面講述的線程池的實(shí)現(xiàn)原理有非常大的關(guān)系绍申。在創(chuàng)建了線程池后噩咪,默認(rèn)情況下,線程池中并沒有任何線程极阅,而是等待有任務(wù)到來才創(chuàng)建線程去執(zhí)行任務(wù)胃碾,除非調(diào)用了prestartAllCoreThreads()或者prestartCoreThread()方法,從這2個方法的名字就可以看出筋搏,是預(yù)創(chuàng)建線程的意思仆百,即在沒有任務(wù)到來之前就創(chuàng)建corePoolSize個線程或者一個線程。默認(rèn)情況下奔脐,在創(chuàng)建了線程池后俄周,線程池中的線程數(shù)為0,當(dāng)有任務(wù)來之后髓迎,就會創(chuàng)建一個線程去執(zhí)行任務(wù)峦朗,當(dāng)線程池中的線程數(shù)目達(dá)到corePoolSize后,就會把到達(dá)的任務(wù)放到緩存隊(duì)列當(dāng)中排龄。

四波势、maximumPoolSize:

線程池最大線程數(shù),這個參數(shù)也是一個非常重要的參數(shù),它表示在線程池中最多能創(chuàng)建多少個線程尺铣;

五拴曲、keepAliveTime:

表示線程沒有任務(wù)執(zhí)行時最多保持多久時間會終止。默認(rèn)情況下凛忿,只有當(dāng)線程池中的線程數(shù)大于corePoolSize時澈灼,keepAliveTime才會起作用,直到線程池中的線程數(shù)不大于corePoolSize侄非,即當(dāng)線程池中的線程數(shù)大于corePoolSize時蕉汪,如果一個線程空閑的時間達(dá)到keepAliveTime,則會終止逞怨,直到線程池中的線程數(shù)不超過corePoolSize者疤。但是如果調(diào)用了allowCoreThreadTimeOut(boolean)方法,在線程池中的線程數(shù)不大于corePoolSize時叠赦,keepAliveTime參數(shù)也會起作用驹马,直到線程池中的線程數(shù)為0;

六除秀、handler:

表示當(dāng)拒絕處理任務(wù)時的策略糯累,有以下四種取值:

//當(dāng)線程池的任務(wù)緩存隊(duì)列已滿并且線程池中的線程數(shù)目達(dá)到maximumPoolSize,如果還有任務(wù)到來就會采取任務(wù)拒絕策略
ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常册踩。 
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務(wù)泳姐,但是不拋出異常。 
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊(duì)列最前面的任務(wù)暂吉,然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程)
ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務(wù) 
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胖秒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子慕的,更是在濱河造成了極大的恐慌阎肝,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,423評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肮街,死亡現(xiàn)場離奇詭異风题,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)嫉父,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評論 2 385
  • 文/潘曉璐 我一進(jìn)店門沛硅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绕辖,你說我怎么就攤上這事稽鞭。” “怎么了引镊?”我有些...
    開封第一講書人閱讀 157,019評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我弟头,道長吩抓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,443評論 1 283
  • 正文 為了忘掉前任赴恨,我火速辦了婚禮疹娶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘伦连。我一直安慰自己雨饺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,535評論 6 385
  • 文/花漫 我一把揭開白布惑淳。 她就那樣靜靜地躺著额港,像睡著了一般。 火紅的嫁衣襯著肌膚如雪歧焦。 梳的紋絲不亂的頭發(fā)上移斩,一...
    開封第一講書人閱讀 49,798評論 1 290
  • 那天,我揣著相機(jī)與錄音绢馍,去河邊找鬼向瓷。 笑死,一個胖子當(dāng)著我的面吹牛舰涌,可吹牛的內(nèi)容都是我干的猖任。 我是一名探鬼主播,決...
    沈念sama閱讀 38,941評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼瓷耙,長吁一口氣:“原來是場噩夢啊……” “哼朱躺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起哺徊,我...
    開封第一講書人閱讀 37,704評論 0 266
  • 序言:老撾萬榮一對情侶失蹤室琢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后落追,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盈滴,經(jīng)...
    沈念sama閱讀 44,152評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,494評論 2 327
  • 正文 我和宋清朗相戀三年轿钠,在試婚紗的時候發(fā)現(xiàn)自己被綠了巢钓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,629評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡疗垛,死狀恐怖症汹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贷腕,我是刑警寧澤背镇,帶...
    沈念sama閱讀 34,295評論 4 329
  • 正文 年R本政府宣布咬展,位于F島的核電站,受9級特大地震影響瞒斩,放射性物質(zhì)發(fā)生泄漏破婆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,901評論 3 313
  • 文/蒙蒙 一胸囱、第九天 我趴在偏房一處隱蔽的房頂上張望祷舀。 院中可真熱鬧,春花似錦烹笔、人聲如沸裳扯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饰豺。三九已至,卻和暖如春柬帕,著一層夾襖步出監(jiān)牢的瞬間哟忍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,978評論 1 266
  • 我被黑心中介騙來泰國打工陷寝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锅很,地道東北人。 一個月前我還...
    沈念sama閱讀 46,333評論 2 360
  • 正文 我出身青樓凤跑,卻偏偏與公主長得像爆安,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子仔引,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,499評論 2 348

推薦閱讀更多精彩內(nèi)容