Java線程池源碼分析

前言

在上一篇文章【Java線程池的使用】中椎工,我們分析了線程池的用法。但那僅僅是用法,關于線程池內部是如何實現(xiàn)的维蒙,我們卻沒有深入分析掰吕。本著知其然,知其所以然的想法颅痊,樓主將嘗試深入到線程池源碼去一窺究竟殖熟。

在jdk里面,線程池最重要的實現(xiàn)是ThreadPoolExecutor斑响。因此菱属,我們分析的重點就是這個類,主要包括線程池狀態(tài)舰罚、線程池變量纽门、構造方法、提交任務等內容营罢。

線程池狀態(tài)

線程池可以包含多個線程赏陵,線程池本身又維護著自己的狀態(tài)。在ThreadPoolExecutor中饲漾,線程池狀態(tài)和worker數(shù)量都是由ctl變量控制蝙搔。

那么,ctl變量又是如何控制線程池狀態(tài)的呢考传?我們先來看看ctl相關的源碼吃型。

// 1. `ctl`,可以看做一個int類型的數(shù)字僚楞,高3位表示線程池狀態(tài)勤晚,低29位表示worker數(shù)量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 2. `COUNT_BITS`,`Integer.SIZE`為32镜硕,所以`COUNT_BITS`為29
private static final int COUNT_BITS = Integer.SIZE - 3;
// 3. `CAPACITY`运翼,線程池允許的最大線程數(shù)返干。1左移29位兴枯,然后減1,即為 2^29 - 1
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// runState is stored in the high-order bits
// 4. 線程池有5種狀態(tài)矩欠,按大小排序如下:RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED
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
// 5. `runStateOf()`财剖,獲取線程池狀態(tài),通過按位與操作癌淮,低29位將全部變成0
private static int runStateOf(int c)     { return c & ~CAPACITY; }
// 6. `workerCountOf()`躺坟,獲取線程池worker數(shù)量,通過按位與操作乳蓄,高3位將全部變成0
private static int workerCountOf(int c)  { return c & CAPACITY; }
// 7. `ctlOf()`咪橙,根據線程池狀態(tài)和線程池worker數(shù)量,生成ctl值
private static int ctlOf(int rs, int wc) { return rs | wc; }

/*
 * Bit field accessors that don't require unpacking ctl.
 * These depend on the bit layout and on workerCount being never negative.
 */
// 8. `runStateLessThan()`,線程池狀態(tài)小于xx
private static boolean runStateLessThan(int c, int s) {
    return c < s;
}
// 9. `runStateAtLeast()`美侦,線程池狀態(tài)大于等于xx
private static boolean runStateAtLeast(int c, int s) {
    return c >= s;
}

在上面的源碼中产舞,每個常量和方法都很關鍵。這兒菠剩,我們對每個關鍵點都進行一下解釋易猫。

  1. ctl,可以看做一個int類型的數(shù)字具壮,高3位表示線程池狀態(tài)准颓,低29位表示線程的數(shù)量
  2. COUNT_BITSInteger.SIZE為32棺妓,所以COUNT_BITS為29
  3. CAPACITY攘已,線程池允許的最大線程數(shù)。1左移29位涧郊,然后減1贯被,即為 2^29 - 1
  4. 線程池有5種狀態(tài),按大小排序如下:RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED
  5. runStateOf()妆艘,獲取線程池狀態(tài)彤灶,通過按位與操作,低29位將全部變成0
  6. workerCountOf()批旺,獲取線程池worker數(shù)量幌陕,通過按位與操作,高3位將全部變成0
  7. ctlOf()汽煮,根據線程池狀態(tài)和線程池worker數(shù)量搏熄,生成ctl值
  8. runStateLessThan(),線程池狀態(tài)小于xx
  9. runStateAtLeast()暇赤,線程池狀態(tài)大于等于xx

為了加深對這段代碼的理解心例,我們將常量對應的二進制數(shù)以表格的形式列出來,如下所示鞋囊。

常量 二進制數(shù)
RUNNING 11100000000000000000000000000000
SHUTDOWN 00000000000000000000000000000000
STOP 00100000000000000000000000000000
TIDYING 01000000000000000000000000000000
TERMINATED 01100000000000000000000000000000
CAPACITY 00011111111111111111111111111111
~CAPACITY 11100000000000000000000000000000

構造方法

ThreadPoolExecutor有多個重載構造方法止后,但是他們最終調用了同一個構造方法。該方法非常簡單溜腐,主要做了兩件事情:

  1. 對輸入參數(shù)進行校驗并設置到成員變量
  2. 根據輸入參數(shù)unitkeepAliveTime译株,將存活時間轉換為納秒存到變量keepAliveTime
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    // 基本類型參數(shù)校驗
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    // 空指針校驗
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    // 根據傳入參數(shù)`unit`和`keepAliveTime`,將存活時間轉換為納秒存到變量`keepAliveTime `中
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

使用線程池執(zhí)行任務 - 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();
    // worker數(shù)量比核心線程數(shù)小挺益,直接創(chuàng)建worker執(zhí)行任務
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // worker數(shù)量超過核心線程數(shù)歉糜,任務直接進入隊列
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        // 線程池狀態(tài)不是RUNNING狀態(tài),說明執(zhí)行過shutdown命令望众,需要對新加入的任務執(zhí)行reject()操作匪补。
        // 這兒為什么需要recheck伞辛,是因為任務入隊列前后,線程池的狀態(tài)可能會發(fā)生變化夯缺。
        if (! isRunning(recheck) && remove(command))
            reject(command);
        // 這兒為什么需要判斷0值始锚,主要是在線程池構造方法中,核心線程數(shù)允許為0
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 如果線程池不是運行狀態(tài)喳逛,或者任務進入隊列失敗瞧捌,則嘗試創(chuàng)建worker執(zhí)行任務。
    // 這兒有3點需要注意:
    // 1. 線程池不是運行狀態(tài)時润文,addWorker內部會判斷線程池狀態(tài)
    // 2. addWorker第2個參數(shù)表示是否創(chuàng)建核心線程
    // 3. addWorker返回false姐呐,則說明任務執(zhí)行失敗,需要執(zhí)行reject操作
    else if (!addWorker(command, false))
        reject(command);
}

我們已經分析完了execute()方法典蝌,其內部調用了一些關鍵方法曙砂,如下所示:

  1. addWorker() // 創(chuàng)建worker
  2. reject() // 拒絕任務

由于reject()較為簡單,我們先分析它骏掀,然后分析addWorker()鸠澈。

拒絕任務 - reject()

private volatile RejectedExecutionHandler handler;

final void reject(Runnable command) {
    handler.rejectedExecution(command, this);
}

只是調用了拒絕策略的回調方法。

添加worker - addWorker()

這個方法可以簡單地分成兩個部分截驮,一部分通過樂觀鎖+自旋的方式增加worker的數(shù)量笑陈,另一部分通過悲觀鎖的方式創(chuàng)建worker(一個worker可以簡單地被認為是線程池里的一個線程)。

先來看第一部分葵袭。

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    // 外層自旋
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // 這個條件寫得比較難懂涵妥,我對其進行了調整,和下面的條件等價
        // (rs > SHUTDOWN) || 
        // (rs == SHUTDOWN && firstTask != null) || 
        // (rs == SHUTDOWN && workQueue.isEmpty())
        // 1. 線程池狀態(tài)大于SHUTDOWN時坡锡,直接返回false
        // 2. 線程池狀態(tài)等于SHUTDOWN蓬网,且firstTask不為null,直接返回false
        // 3. 線程池狀態(tài)等于SHUTDOWN鹉勒,且隊列為空帆锋,直接返回false
        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;

        // 內層自旋
        for (;;) {
            int wc = workerCountOf(c);
            // worker數(shù)量超過容量,直接返回false
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // 使用CAS的方式增加worker數(shù)量禽额。
            // 若增加成功锯厢,則直接跳出外層循環(huán)進入到第二部分
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            // 線程池狀態(tài)發(fā)生變化,對外層循環(huán)進行自旋
            if (runStateOf(c) != rs)
                continue retry;
            // 其他情況绵疲,直接內層循環(huán)進行自旋即可
            // else CAS failed due to workerCount change; retry inner loop
        }
    }
    // ...
}

接下來我們分析第二部分哲鸳。

private boolean addWorker(Runnable firstTask, boolean core) {
    // ...
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            // worker的添加必須是串行的臣疑,因此需要加鎖
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                // 這兒需要重新檢查線程池狀態(tài)
                int rs = runStateOf(ctl.get());

                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    // worker已經調用過了start()方法盔憨,則不再創(chuàng)建worker
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    // worker創(chuàng)建并添加到workers成功
                    workers.add(w);
                    // 更新`largestPoolSize`變量
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            // 啟動worker線程
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        // worker線程啟動失敗,說明線程池狀態(tài)發(fā)生了變化(關閉操作被執(zhí)行)讯沈,需要進行shutdown相關操作
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

通過對第一部分和第二部分的分析郁岩,我們知道了線程池中的線程是封裝在Worker中執(zhí)行的婿奔。下一步,我們就從Worker進行分析问慎。

線程池執(zhí)行單元 - Worker

private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{
    /**
     * This class will never be serialized, but we provide a
     * serialVersionUID to suppress a javac warning.
     */
    private static final long serialVersionUID = 6138294804551838833L;

    /** Thread this worker is running in.  Null if factory fails. */
    final Thread thread;
    /** Initial task to run.  Possibly null. */
    Runnable firstTask;
    /** Per-thread task counter */
    volatile long completedTasks;

    /**
     * Creates with given first task and thread from ThreadFactory.
     * @param firstTask the first task (null if none)
     */
    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        // 這兒是Worker的關鍵所在萍摊,使用了線程工廠創(chuàng)建了一個線程。傳入的參數(shù)為當前worker
        this.thread = getThreadFactory().newThread(this);
    }

    /** Delegates main run loop to outer runWorker  */
    public void run() {
        runWorker(this);
    }

    // 省略AQS相關的代碼...
}

上述代碼為Worker的基本實現(xiàn)(省略了AQS相關的代碼)如叼。我們看到Worker實現(xiàn)了兩個接口冰木,RunnableAbstractQueuedSynchronizer

關于AQS相關的知識笼恰,我們在AbstractQueuedSynchronizer(AQS)源碼解析做了比較全面的說明踊沸。因此,這兒我們更多的是關心run()方法的實現(xiàn)社证。

run()方法其實調用了ThreadPoolExecutor.runWorker()方法逼龟,且把當前Worker對象傳遞了進去。

核心線程執(zhí)行邏輯 - runWorker()

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    // 調用unlock()是為了讓外部可以中斷
    w.unlock(); // allow interrupts
    // 這個變量用于判斷是否進入過自旋(while循環(huán))
    boolean completedAbruptly = true;
    try {
        // 這兒是自旋
        // 1. 如果firstTask不為null追葡,則執(zhí)行firstTask腺律;
        // 2. 如果firstTask為null,則調用getTask()從隊列獲取任務宜肉。
        // 3. 阻塞隊列的特性就是:當隊列為空時匀钧,當前線程會被阻塞等待
        while (task != null || (task = getTask()) != null) {
            // 這兒對worker進行加鎖,是為了達到下面的目的
            // 1. 降低鎖范圍谬返,提升性能
            // 2. 保證每個worker執(zhí)行的任務是串行的
            w.lock();
            // If pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This
            // requires a recheck in second case to deal with
            // shutdownNow race while clearing interrupt
            // 如果線程池正在停止榴捡,則對當前線程進行中斷操作
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            // 執(zhí)行任務,且在執(zhí)行前后通過`beforeExecute()`和`afterExecute()`來擴展其功能朱浴。
            // 這兩個方法在當前類里面為空實現(xiàn)吊圾。
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                // 幫助gc
                task = null;
                // 已完成任務數(shù)加一 
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        // 自旋操作被退出,說明線程池正在結束
        processWorkerExit(w, completedAbruptly);
    }
}

該方法是線程池執(zhí)行任務的關鍵所在翰蠢。雖然是核心功能项乒,但是本身的邏輯卻非常簡單和高效。該方法主要通過自旋梁沧、阻塞隊列的阻塞特性和worker加鎖的方式來完成任務的執(zhí)行檀何。除此之外,使用了finally機制來釋放資源(處理線程池結束的后續(xù)操作)廷支。

worker結束處理邏輯 - processWorkerExit

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    // 這個變量用于表示是否進入過自旋频鉴。
    // 1. 如果沒有進入過,該值為false
    // 2. 進入過恋拍,該值為true
    // 只有進入過自旋垛孔,worker的數(shù)量才需要減一
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();

    // 通過全局鎖的方式移除worker
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        completedTaskCount += w.completedTasks;
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }

    // 嘗試終止線程池
    tryTerminate();

    int c = ctl.get();
    // 如果線程池狀態(tài)為`SHUTDOWN`或`RUNNING`,
    // 則通過調用`addWorker()`來創(chuàng)建線程施敢,輔助完成對阻塞隊列中任務的處理周荐。
    if (runStateLessThan(c, STOP)) {
        if (!completedAbruptly) {
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        addWorker(null, false);
    }
}

該方法主要通過全局鎖的方式來移除當前worker狭莱,同時如果線程池狀態(tài)是SHUTDOWNRUNNING時,則通過調用addWorker()來創(chuàng)建線程概作,輔助完成對阻塞隊列中任務的處理腋妙。

線程池的退出

線程池退出的方式有兩種,一種為優(yōu)雅型讯榕,一種為強迫型骤素。

  1. 優(yōu)雅型會等待所有已提交到線程池的任務執(zhí)行完成之后才結束線程池。
  2. 強迫型會對當前正在執(zhí)行的任務進行中斷操作愚屁,并將阻塞隊列里面的任務以List<Runnable>的方式返回給調用方谆甜,由調用方做后續(xù)處理。

優(yōu)雅型退出 - shutdown()

public void shutdown() {
    // 使用全局鎖來操作線程池終止
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 權限校驗
        checkShutdownAccess();
        // 通過CAS的方式變更線程池狀態(tài)為`SHUTDOWN`
        advanceRunState(SHUTDOWN);
        // 中斷空閑的worker集绰,這也是和`shutdownNow()`不同的地方
        interruptIdleWorkers();
        // 留給ScheduledThreadPoolExecutor處理
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}

關鍵的地方為interruptIdleWorkers()规辱,該方法用于中斷空閑的worker。我們來看看這個方法栽燕,然后對其進行補充說明罕袋。

private void interruptIdleWorkers() {
    interruptIdleWorkers(false);
}

// 1. `onlyOne`用于控制是否只循環(huán)一次,在`shutdown()`里面碍岔,該值為false浴讯,因此是遍歷整個worker列表
private void interruptIdleWorkers(boolean onlyOne) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers) {
            Thread t = w.thread;
            // 2. 對每個worker,會嘗試去獲取worker鎖蔼啦。
            //    如果獲取到了榆纽,那么說明該worker是空閑的,可以進行中斷操作
            if (!t.isInterrupted() && w.tryLock()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                } finally {
                    w.unlock();
                }
            }
            if (onlyOne)
                break;
        }
    } finally {
        mainLock.unlock();
    }
}

interruptIdleWorkers()做了兩件事情:

  1. onlyOne用于控制是否只循環(huán)一次捏肢,在shutdown()里面奈籽,該值為false,因此是遍歷整個worker列表
  2. 對每個worker鸵赫,會嘗試去獲取worker鎖衣屏。如果獲取到了,那么說明該worker是空閑的辩棒,可以進行中斷操作

強迫型退出 - shutdownNow()

public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    // 使用全局鎖來操作線程池終止
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 權限校驗
        checkShutdownAccess();
        // 通過CAS的方式變更線程池狀態(tài)為`STOP`
        advanceRunState(STOP);
        // 中斷所有的worker狼忱,這也是和`shutdown()`不同的地方
        interruptWorkers();
        // 將阻塞隊列里面的任務排出到tasks
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}

該方法和shutdown()不同的地方在于對worker的處理方式。shutdownNow()處理得非常得強勢一睁,調用了interruptWorkers()方法钻弄。我們來看看此方法,然后作出說明者吁。

private void interruptWorkers() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 1. 對所有worker調用`interruptIfStarted()`方法
        for (Worker w : workers)
            w.interruptIfStarted();
    } finally {
        mainLock.unlock();
    }
}

private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{
    void interruptIfStarted() {
        Thread t;
        // 2.線程池狀態(tài)為`非RUNNING`時窘俺,直接對worker包裝的線程進行中斷操作
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    }
}

該方法做了兩件事情:

  1. 對所有worker調用interruptIfStarted()方法
  2. 線程池狀態(tài)為非RUNNING時,直接對worker包裝的線程進行中斷操作

線程池狀態(tài)變更 - advanceRunState()

shutdown()shutdownNow()中砚偶,我們都看到了對advanceRunState()方法的調用批销。該方法用于線程安全地變更線程池狀態(tài),那么它是如何做到線程安全的呢染坯?

private void advanceRunState(int targetState) {
    // 1. 自旋操作
    for (;;) {
        // 2. 通過CAS的方式保證線程池狀態(tài)變更的線程安全性
        int c = ctl.get();
        if (runStateAtLeast(c, targetState) ||
            ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
            break;
    }
}

PS:我們可能很好奇均芽,advanceRunState()方法為什么要用CAS呢?在shutdown()shutdownNow()里面不是有加鎖嗎单鹿?
請注意掀宋,外層方法的確有加鎖,但是和worker線程所使用的鎖并不是同一個仲锄,因此需要通過自旋+CAS的方式來額外保證線程安全劲妙!

阻塞隊列任務排出 - drainQueue()

private List<Runnable> drainQueue() {
    BlockingQueue<Runnable> q = workQueue;
    ArrayList<Runnable> taskList = new ArrayList<Runnable>();
    q.drainTo(taskList);
    if (!q.isEmpty()) {
        for (Runnable r : q.toArray(new Runnable[0])) {
            if (q.remove(r))
                taskList.add(r);
        }
    }
    return taskList;
}

該方法通過兩個階段來保證導出任務的完整性。

  1. 調用阻塞隊列的drainTo()方法
  2. 遍歷阻塞隊列剩余的元素儒喊,通過remove()&add()的方式來導出元素镣奋。

drainTo()的過程中可能有新元素進來,這也是需要兩個階段來完成導出的原因怀愧。

帶返回結果的任務提交 - submit()

 public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    // ftask既是Runnable侨颈,也是Future
    RunnableFuture<T> ftask = newTaskFor(task);
    // 執(zhí)行任務
    execute(ftask);
    return ftask;
}

protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable {
    return new FutureTask<T>(callable);
}

submit()內部調用的仍然是execute()方法,只是調完之后返回了Future對象芯义,我們可以通過這個對象的get()方法來拿到最終執(zhí)行的結果哈垢。

到這里,我們必須打住扛拨,F(xiàn)uture相關的知識點不在本節(jié)的討論范圍內耘分。

因為FutureFutureTask非常重要,樓主計劃單獨寫一篇博客來做闡述绑警。Future相關的東西比較多求泰,而且復雜。為了不影響本問的初衷计盒,至此暫且略過拜秧。

總結

本文通過對Java線程池代碼的分析,使得我們知道了其大概的實現(xiàn)方式章郁。

在Java線程池出現(xiàn)之前枉氮,如果我們想異步執(zhí)行一個任務,往往是先創(chuàng)建一個線程暖庄,然后執(zhí)行任務聊替,執(zhí)行完之后就扔掉了,最后等待GC對創(chuàng)建線程的回收培廓。而這樣處理的后果惹悄,往往是非常昂貴的內存和CPU開銷。

Java線程池比較徹底地解決了這個問題肩钠!其內部實現(xiàn)合理地運用了自旋泣港、CAS暂殖、樂觀鎖、悲觀鎖当纱、recheck重測呛每、狀態(tài)機、原子變量等等各種并發(fā)技術坡氯,充分有效地發(fā)揮了CPU的多核計算能力晨横。由此可見并發(fā)大師Doug Lea設計和編碼的功力之深,簡直是前無古人箫柳,后無來者手形?

我們將Java線程池的分析放在這一篇博客中,其實有點管中窺豹之嫌悯恍。很多的細節(jié)库糠,我們往往是一筆代過。但是涮毫,大致的結構和功能卻是分析到了的曼玩。

最后,樓主希望本文能對讀者有所幫助窒百!

參考鏈接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末黍判,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子篙梢,更是在濱河造成了極大的恐慌顷帖,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渤滞,死亡現(xiàn)場離奇詭異贬墩,居然都是意外死亡,警方通過查閱死者的電腦和手機妄呕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門陶舞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绪励,你說我怎么就攤上這事肿孵。” “怎么了疏魏?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵停做,是天一觀的道長。 經常有香客問我大莫,道長蛉腌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮烙丛,結果婚禮上舅巷,老公的妹妹穿的比我還像新娘。我一直安慰自己河咽,他們只是感情好钠右,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著库北,像睡著了一般爬舰。 火紅的嫁衣襯著肌膚如雪们陆。 梳的紋絲不亂的頭發(fā)上寒瓦,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音坪仇,去河邊找鬼杂腰。 笑死,一個胖子當著我的面吹牛椅文,可吹牛的內容都是我干的喂很。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼皆刺,長吁一口氣:“原來是場噩夢啊……” “哼少辣!你這毒婦竟也來了?” 一聲冷哼從身側響起羡蛾,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤漓帅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后痴怨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體忙干,經...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年浪藻,在試婚紗的時候發(fā)現(xiàn)自己被綠了捐迫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡爱葵,死狀恐怖施戴,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情萌丈,我是刑警寧澤暇韧,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站浓瞪,受9級特大地震影響懈玻,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一涂乌、第九天 我趴在偏房一處隱蔽的房頂上張望艺栈。 院中可真熱鬧,春花似錦湾盒、人聲如沸湿右。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毅人。三九已至,卻和暖如春尖殃,著一層夾襖步出監(jiān)牢的瞬間丈莺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工送丰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缔俄,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓器躏,卻偏偏與公主長得像俐载,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子登失,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內容