Java 線程池解析--新提交任務(wù)如何被分配給空閑線程

最近工作中使用了Java線程池,然后想弄清楚新提交的任務(wù)是如何被分配給線程池中的空余線程的唧取,于是就看了下ThreadPoolExecutor的源碼铅鲤。以下面代碼為例,進(jìn)行debug枫弟。

public class ThreadPoolDemo {


    public static void main(String args[]){
        ExecutorService pool = Executors.newFixedThreadPool(2);
        Printer a = new Printer("I am A");
        Printer b = new Printer("I am B");
        pool.submit(a);
        pool.submit(b);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Printer c = new Printer("I am C");
        pool.submit(c);

        pool.shutdown();
    }
}

class Printer implements Runnable {

    private String msg;

    public Printer(String msg){
        this.msg = msg;
    }

    public void run()  {

        System.out.println(msg);
    }
}

首先當(dāng)我們提交任務(wù)時邢享,調(diào)用的是AbstractExecutorService的submit方法

 /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

然后會執(zhí)行ThreadPoolExecutor的execute方法

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

接著執(zhí)行addWorker方法,addWorker的大概邏輯就是淡诗,如果線程池中的線程數(shù)小于corePoolSize骇塘,那么就新建線程執(zhí)行任務(wù),例子中當(dāng)a,b任務(wù)提交后韩容,我們的線程池中就保持了兩個可用的線程款违,當(dāng)c任務(wù)提交后,是如何處理的呢群凶?

在ThreadPoolExecutor里有runWorker方法插爹,在while循環(huán)里如果getTask()能取到任務(wù),那么就讓W(xué)orker w不停的執(zhí)行task请梢。

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                   ......
                   此處代碼部分省略
                    //任務(wù)執(zhí)行
                    task.run();
                   此處代碼部分省略
                    ......
                   }
        } 
    }

所以問題的關(guān)鍵就在于getTask()方法赠尾。

private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

其中下面這行代碼揭示了最終問題的答案。

Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();

workQueue是線程池用來存放任務(wù)的阻塞隊(duì)列毅弧,workQueue.take()在隊(duì)列中沒有任務(wù)的時候會等待气嫁,一旦有新的任務(wù)被提交,r就會獲取到這個最新的任務(wù)够坐,并且被返回給runWorker執(zhí)行任務(wù)寸宵。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市咆霜,隨后出現(xiàn)的幾起案子邓馒,更是在濱河造成了極大的恐慌,老刑警劉巖蛾坯,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件光酣,死亡現(xiàn)場離奇詭異,居然都是意外死亡脉课,警方通過查閱死者的電腦和手機(jī)救军,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門财异,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人唱遭,你說我怎么就攤上這事戳寸。” “怎么了拷泽?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵疫鹊,是天一觀的道長。 經(jīng)常有香客問我司致,道長拆吆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任脂矫,我火速辦了婚禮枣耀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘庭再。我一直安慰自己捞奕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布拄轻。 她就那樣靜靜地躺著颅围,像睡著了一般。 火紅的嫁衣襯著肌膚如雪哺眯。 梳的紋絲不亂的頭發(fā)上谷浅,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天,我揣著相機(jī)與錄音奶卓,去河邊找鬼一疯。 笑死,一個胖子當(dāng)著我的面吹牛夺姑,可吹牛的內(nèi)容都是我干的墩邀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼盏浙,長吁一口氣:“原來是場噩夢啊……” “哼眉睹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起废膘,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤竹海,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后丐黄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斋配,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了艰争。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坏瞄。...
    茶點(diǎn)故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖甩卓,靈堂內(nèi)的尸體忽然破棺而出鸠匀,到底是詐尸還是另有隱情,我是刑警寧澤逾柿,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布缀棍,位于F島的核電站,受9級特大地震影響机错,放射性物質(zhì)發(fā)生泄漏睦柴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一毡熏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧侣诵,春花似錦痢法、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至躬络,卻和暖如春尖奔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背穷当。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工提茁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人馁菜。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓茴扁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親汪疮。 傳聞我的和親對象是個殘疾皇子峭火,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評論 2 355

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