Java 理解 ThreadPoolExecutor 實現(xiàn)原理

使用線程池(ThreadPoolExecutor)的好處是減少在創(chuàng)建和銷毀線程上所花的時間以及系統(tǒng)資源的開銷,解決資源不足的問題棍辕。如果不使用線程池,有可能造成系統(tǒng)創(chuàng)建大量同類線程而導(dǎo)致消耗完內(nèi)存或者“過度切換”的問題还绘。 -- 阿里Java開發(fā)手冊

版本

JDK 1.8

本節(jié)目標(biāo)

  • 理解線程池核心參數(shù)
  • 理解線程池工作原理
  • 理解線程池核心方法

線程池的核心參數(shù)和構(gòu)造方法

ctl
    // 線程池核心變量楚昭,包含線程池的運行狀態(tài)和有效線程數(shù),利用二進制的位掩碼實現(xiàn)
    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
    // 線程池狀態(tài)
    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
    // 獲取當(dāng)前線程池運行狀態(tài)
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    // 獲取當(dāng)前線程池有效線程數(shù)
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    // 打包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.
     */

    private static boolean runStateLessThan(int c, int s) {
        return c < s;
    }

    private static boolean runStateAtLeast(int c, int s) {
        return c >= s;
    }

    private static boolean isRunning(int c) {
        return c < SHUTDOWN;
    }

JDK7 以后拍顷,線程池的狀態(tài)和有效線程數(shù)通過 ctl 這個變量表示(使用二進制的位掩碼來實現(xiàn)抚太,這里我們不深究),理解上述幾個方法作用即可昔案,不影響下面的源碼閱讀

關(guān)于線程池的五種狀態(tài)
  • RUNNING:接受新任務(wù)并處理隊列中的任務(wù)

  • SHUTDOWN :不接受新任務(wù)尿贫,但處理隊列中的任務(wù)

  • STOP :不接受新任務(wù),不處理隊列中的任務(wù)踏揣,并中斷正在進行的任務(wù)(中斷并不是強制的庆亡,只是修改了Thread的狀態(tài),是否中斷取決于Runnable 的實現(xiàn)邏輯)

  • TIDYING :所有任務(wù)都已終止捞稿,workerCount為0時又谋,線程池會過度到該狀態(tài)钝尸,并即將調(diào)用 terminate()

  • TERMINATED :terminated() 調(diào)用完成;線程池中止

線程池狀態(tài)的轉(zhuǎn)換
  • RUNNING => SHUTDOWN :調(diào)用 shutdown()

  • RUNNING / SHUTDOWN => STOP :調(diào)用 shutdownNow() (該方法會返回隊列中未執(zhí)行的任務(wù))

  • SHUTDOWN => TIDYING: 當(dāng)線程池和隊列都為空時

  • STOP => TIDYING:當(dāng)線程池為空時

  • TIDYING => TERMINATED:當(dāng) terminated() 調(diào)用完成時

構(gòu)造方法

線程池最終都是調(diào)用如下構(gòu)造方法

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
  // 省略
}
核心參數(shù)

我們來看一下線程池中的核心參數(shù)都是什么作用

private final BlockingQueue<Runnable> workQueue; // 阻塞隊列搂根,用于緩存任務(wù)

private final ReentrantLock mainLock = new ReentrantLock(); // 線程池主鎖

private final HashSet<Worker> workers = new HashSet<Worker>(); // 工作線程集合

private final Condition termination = mainLock.newCondition(); // awaitTermination() 方法的等待條件

private int largestPoolSize; // 記錄最大線程池大小

private long completedTaskCount; //用來記錄線程池中曾經(jīng)出現(xiàn)過的最大線程數(shù)

private volatile ThreadFactory threadFactory; // 線程工廠珍促,用于創(chuàng)建線程

private volatile RejectedExecutionHandler handler; // 任務(wù)拒絕時的策略

private volatile long keepAliveTime; // 線程存活時間
                                     // 當(dāng)線程數(shù)超過核心池數(shù)時,或允許核心池線程超時剩愧,該參數(shù)會起作用猪叙。否則一直會等待新的任務(wù)

private volatile boolean allowCoreThreadTimeOut; // 是否允許核心池線程超時

private volatile int corePoolSize; // 核心線程池數(shù)量

private volatile int maximumPoolSize; // 最大線程池數(shù)量
workQueue

這個隊列的作用,和之前的 Java 理解生產(chǎn)者-消費者設(shè)計模式 中講到的緩沖隊列仁卷,作用很相似穴翩,或者說線程池就是生產(chǎn)者消費者模式的一種實現(xiàn)。

關(guān)于 handler
  • ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常锦积。
  • ThreadPoolExecutor.DiscardPolicy:也是丟棄任務(wù)芒帕,但是不拋出異常。
  • ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務(wù)丰介,然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程)
  • ThreadPoolExecutor.CallerRunsPolicy:當(dāng)前任務(wù)自己決定
corePoolSize 和 maximumPoolSize

如果你對這兩個參數(shù)有疑問背蟆,看完下面的栗子你會清晰很多

下面我們來舉個栗子來更好的理解一下線程池

理解線程池工作原理

假如有一個工廠,工廠里面有10個工人哮幢,每個工人同時只能做一件任務(wù)带膀。

因此只要當(dāng)10個工人中有工人是空閑的,來了任務(wù)就分配給空閑的工人做橙垢;

當(dāng)10個工人都有任務(wù)在做時垛叨,如果還來了任務(wù),就把任務(wù)進行排隊等待柜某;

每個工人做完自己的任務(wù)后嗽元,會去任務(wù)隊列中領(lǐng)取新的任務(wù);

如果說新任務(wù)數(shù)目增長的速度遠遠大于工人做任務(wù)的速度(任務(wù)累積過多時)喂击,那么此時工廠主管可能會想補救措施剂癌,比如重新招4個臨時工人進來;

然后就將任務(wù)也分配給這4個臨時工人做惭等;

如果說著14個工人做任務(wù)的速度還是不夠珍手,此時工廠主管可能就要考慮不再接收新的任務(wù)或者拋棄前面的一些任務(wù)了。

當(dāng)這14個工人當(dāng)中有人空閑時辞做,而新任務(wù)增長的速度又比較緩慢琳要,工廠主管可能就考慮辭掉4個臨時工了,只保持原來的10個工人秤茅,畢竟請額外的工人是要花錢的稚补。


  • 開始工廠的10個工人,就是 corePoolSize (核心池數(shù)量)框喳;

  • 當(dāng)10個人都在工作時 (核心池達到 corePoolSize)课幕,任務(wù)排隊等待時厦坛,會緩存到 workQueue 中;

  • 當(dāng)任務(wù)累積過多時(達到 workQueue 最大值時)乍惊,找臨時工杜秸;

  • 14個臨時工,就是 maximumPoolSize (數(shù)量)润绎;

  • 如果此時工作速度還是不夠撬碟,線程池這時會考慮拒絕任務(wù),具體由拒絕策略決定

理解線程池核心方法

execute()

線程池中所有執(zhí)行任務(wù)的方法有關(guān)的方法莉撇,都會調(diào)用 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);
}
分析execute()
  • step 1

1)首先檢查當(dāng)前有效線程數(shù) 是否小于 核心池數(shù)量
if (workerCountOf(c) < corePoolSize)

2)如果滿足上述條件棍郎,則嘗試向核心池添加一個工作線程 (addWorker() 第二個參數(shù)決定了是添加核心池其障,還是最大池)
if (addWorker(command, true))

3)如果成功則退出方法,否則將執(zhí)行 step2

  • step 2

1)如果當(dāng)前線程池處于運行狀態(tài) && 嘗試向緩沖隊列添加任務(wù)
if (isRunning(c) && workQueue.offer(command))

2)如果線程池正在運行并且緩沖隊列添加任務(wù)成功涂佃,進行 double check(再次檢查)

3)如果此時線程池非運行狀態(tài) => 移除隊列 => 拒絕當(dāng)前任務(wù)励翼,退出方法
(這么做是為了,當(dāng)線程池不可用時及時回滾)

if (! isRunning(recheck) && remove(command))
    reject(command);

4)如果當(dāng)前有效線程數(shù)為0巡李,則創(chuàng)建一個無任務(wù)的工作線程(此時這個線程會去隊列中獲取任務(wù))

  • step 3

1)當(dāng)無法無法向核心池和隊列中添加任務(wù)時抚笔,線程池會再嘗試向最大池中添加一個工作線程,如果失敗則拒絕該任務(wù)

else if (!addWorker(command, false))
         reject(command);
圖解execute()

根據(jù)上述的步驟畫了如下的這個圖侨拦,希望能幫助大家更好的理解

image.png
addWorker()

在分析execute() 方法時,我們已經(jīng)知道了 addWorker() 的作用了辐宾,可以向核心池或者最大池添加一個工作線程狱从。我們來看一下這個方法都做了什么

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

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;

        for (;;) {
            int wc = workerCountOf(c);
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }

    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;
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                int rs = runStateOf(ctl.get());

                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

這個方法代碼看似很復(fù)雜,沒關(guān)系叠纹,我們一步一步來分析

  • step 1
    先看第一部分
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;

        for (;;) {
            int wc = workerCountOf(c);
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }

這一部分代碼季研,主要是判斷,是否可以添加一個工作線程誉察。

在execute()中已經(jīng)判斷過if (workerCountOf(c) < corePoolSize)了与涡,為什么還要再判斷?

因為在多線程環(huán)境中持偏,當(dāng)上下文切換到這里的時候驼卖,可能線程池已經(jīng)關(guān)閉了,或者其他線程提交了任務(wù)鸿秆,導(dǎo)致workerCountOf(c) > corePoolSize

1)首先進入第一個無限for循環(huán)酌畜,獲取ctl對象,獲取當(dāng)前線程的運行狀態(tài)卿叽,然后判斷

if (rs >= SHUTDOWN &&
    ! (rs == SHUTDOWN &&
       firstTask == null &&
       ! workQueue.isEmpty()))
    return false;

這個判斷的意義為桥胞,當(dāng)線程池運行狀態(tài) >= SHUTDOWN 時恳守,向添加一個工作線程必須同時滿足

  • rs == SHUTDOWN
  • firstTask == null
  • ! workQueue.isEmpty()
    三個條件,否則添加線程失敗

所以當(dāng)線程狀態(tài)為SHUTDOWN時贩虾,線程池允許添加一個無任務(wù)的工作線程去執(zhí)行隊列中的任務(wù)催烘。

2)進入第二個無限for循環(huán)

for (;;) {
    int wc = workerCountOf(c);
    if (wc >= CAPACITY ||
        wc >= (core ? corePoolSize : maximumPoolSize))
        return false;
    if (compareAndIncrementWorkerCount(c))
        break retry;
    c = ctl.get();  // Re-read ctl
    if (runStateOf(c) != rs)
        continue retry;
    // else CAS failed due to workerCount change; retry inner loop
}

獲取當(dāng)前有效線程數(shù),if 有效線程數(shù) >= 容量 || 有效線程數(shù) >= 核心池數(shù)量/最大池數(shù)量缎罢,則return false; 添加線程失敗

如果有效線程數(shù)在合理范圍之內(nèi)伊群,嘗試使用 CAS 自增有效線程數(shù) (CAS 是Java中的樂觀鎖,不了解的小伙伴可以Google一下)屁使,樂觀鎖自增成功在岂,代表當(dāng)前無其他線程競爭,相當(dāng)于獲取到鎖了

如果自增成功蛮寂,break retry; 跳出這兩個循環(huán)蔽午,執(zhí)行下面的代碼

自增失敗,檢查線程池狀態(tài)酬蹋,如果線程池狀態(tài)發(fā)生變化及老,回到第一個for 繼續(xù)執(zhí)行;否則繼續(xù)在第二個for 中范抓;

  • step 2
    下面這部分就比較簡單了
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;
        mainLock.lock();
        try {
            // Recheck while holding lock.
            // Back out on ThreadFactory failure or if
            // shut down before lock acquired.
            int rs = runStateOf(ctl.get());

            if (rs < SHUTDOWN ||
                (rs == SHUTDOWN && firstTask == null)) {
                if (t.isAlive()) // precheck that t is startable
                    throw new IllegalThreadStateException();
                workers.add(w);
                int s = workers.size();
                if (s > largestPoolSize)
                    largestPoolSize = s;
                workerAdded = true;
            }
        } finally {
            mainLock.unlock();
        }
        if (workerAdded) {
            t.start();
            workerStarted = true;
        }
    }
} finally {
    if (! workerStarted)
        addWorkerFailed(w);
}
return workerStarted;

1)創(chuàng)建工作線程對象Worker骄恶;

2)加鎖,判斷當(dāng)前線程池狀態(tài)是否允許啟動線程匕垫;
如果可以僧鲁,將線程加入workers(這個變量在需要遍歷所有工作線程時會用到),記錄最大值象泵,啟動線程寞秃;

3)如果線程啟動失敗,執(zhí)行addWorkerFailed(從workers中移除該對象偶惠,有效線程數(shù)減一春寿,嘗試中止線程池)

Worker

Worker對象是線程池中的內(nèi)部類,線程的復(fù)用忽孽、線程超時都是在這實現(xiàn)的

private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{
    // 這里我們只關(guān)心Run()绑改,省略了其他源碼,感興趣的同學(xué)可以自己看一下源碼
    public void run() {
        runWorker(this);
    }
}

Worker 實現(xiàn)了 Runnable兄一,我們這里只關(guān)心 Worker 的run方法中做了什么厘线,關(guān)于 AbstractQueuedSynchronizer 有關(guān)的不在本文討論


下面我們分析一下runWorker()

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.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();
            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 {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        // 通過該變量判斷是用戶任務(wù)拋出異常結(jié)束,還是線程池自然結(jié)束
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

1)

 while (task != null || (task = getTask()) != null) {
// ...
}

不對地通過getTask() 從隊列中獲取任務(wù)瘾腰,可以間接通過getTask()的返回值控制線程的結(jié)束

2)

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

接下來這個判斷皆的,其實我是沒有太理解的,暫且認為是保證當(dāng)線程池STOP時蹋盆,線程一定會被打斷

3)執(zhí)行Runnable

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 {
    task = null;
    w.completedTasks++;
    w.unlock();
}

beforeExecute(wt, task); 和 afterExecute(task, thrown); 默認是沒有實現(xiàn)的费薄,我們可以自己擴展

4)最后是當(dāng)跳出while循環(huán)后(getTask() == null或者用戶任務(wù)拋出異常)硝全,會去執(zhí)行processWorkerExit(w, completedAbruptly);線程退出工作(該方法會根據(jù)線程池狀態(tài),嘗試中止線程池楞抡。然后會考慮是結(jié)束當(dāng)前線程伟众,還是再新建一個工作線程,這里就不細說了)


我們再來看一下 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;
        }
    }
}

1) 第一段不做解釋召廷,滿足該條件時凳厢,return null; 退出線程

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

2) 下面這段很有意思

int wc = workerCountOf(c);

// Are workers subject to culling?
// 是否允許線程超時
// 當(dāng)我們設(shè)置了允許核心池超時 或者 有效線程數(shù) > 核心池數(shù)量的時候
// 線程池會考慮為我們清除掉一些線程
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

// (有效線程數(shù) > 最大線程池數(shù)量 || (允許超時 && 超時) ) 
//  && (有效線程數(shù) > 1 || 或者隊列為空時)
if ((wc > maximumPoolSize || (timed && timedOut)) // timedOut 表示當(dāng)前線程超時,下文會說到
    && (wc > 1 || workQueue.isEmpty())) {
    if (compareAndDecrementWorkerCount(c))
        return null;
    continue;
}

我在第一次看這段代碼的時候竞慢,傻傻的以為 timedOut 不是永遠為false嗎先紫,我以為JDK源碼怎么寫出這么個Bug。別忘了當(dāng)前的getTask()方法也是在一個無限循環(huán)里

3)

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

根據(jù) timed筹煮,決定調(diào)用使用poll() 或者 take()遮精。

  • poll 在隊列為空時會等待指定時間,如果這期間沒有獲取到元素败潦,則return null
  • take 則在隊列為空時會一直等待本冲,直至隊列中被添加新的任務(wù),或者被打斷劫扒;
    這兩個方法都會被shutdown() 或者 shutdownNow的 thread.interrupt()打斷檬洞;
    如果被打斷則回到第一步

至此 execute() 方法所涉及的邏輯我們差不多分析完了

備注

線程池使用
public class Test {

    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5));

        executor.execute(() -> {
            // 業(yè)務(wù)邏輯
        });

        executor.shutdown();
    }

}
合理配置線程池的大小

一般需要根據(jù)任務(wù)的類型來配置線程池大小:

如果是CPU密集型任務(wù)沟饥,參考值可以設(shè)為 N+1 (N 為CPU核心數(shù))

如果是IO密集型任務(wù)添怔,參考值可以設(shè)置為2*N

當(dāng)然,這只是一個參考值贤旷,具體的設(shè)置還需要根據(jù)實際情況進行調(diào)整澎灸,比如可以先將線程池大小設(shè)置為參考值,再觀察任務(wù)運行情況和系統(tǒng)負載遮晚、資源利用率來進行適當(dāng)調(diào)整。

參考
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拦止,一起剝皮案震驚了整個濱河市县遣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌汹族,老刑警劉巖萧求,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鬼贱,死亡現(xiàn)場離奇詭異袁翁,居然都是意外死亡兴枯,警方通過查閱死者的電腦和手機颈走,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門稽穆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人车伞,你說我怎么就攤上這事碧查。” “怎么了耗帕?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵穆端,是天一觀的道長。 經(jīng)常有香客問我仿便,道長体啰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任嗽仪,我火速辦了婚禮荒勇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘闻坚。我一直安慰自己沽翔,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布鲤氢。 她就那樣靜靜地躺著搀擂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪卷玉。 梳的紋絲不亂的頭發(fā)上哨颂,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天,我揣著相機與錄音相种,去河邊找鬼威恼。 笑死,一個胖子當(dāng)著我的面吹牛寝并,可吹牛的內(nèi)容都是我干的箫措。 我是一名探鬼主播,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼衬潦,長吁一口氣:“原來是場噩夢啊……” “哼斤蔓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起镀岛,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤弦牡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后漂羊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驾锰,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年走越,在試婚紗的時候發(fā)現(xiàn)自己被綠了椭豫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖赏酥,靈堂內(nèi)的尸體忽然破棺而出喳整,到底是詐尸還是另有隱情,我是刑警寧澤今缚,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布算柳,位于F島的核電站,受9級特大地震影響姓言,放射性物質(zhì)發(fā)生泄漏瞬项。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一何荚、第九天 我趴在偏房一處隱蔽的房頂上張望囱淋。 院中可真熱鬧,春花似錦餐塘、人聲如沸妥衣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽税手。三九已至,卻和暖如春需纳,著一層夾襖步出監(jiān)牢的瞬間芦倒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工不翩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留兵扬,地道東北人。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓口蝠,卻偏偏與公主長得像器钟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子妙蔗,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,974評論 2 355

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

  • 第一部分 來看一下線程池的框架圖眉反,如下: 1狞谱、Executor任務(wù)提交接口與Executors工具類 Execut...
    壓抑的內(nèi)心閱讀 4,266評論 1 24
  • 線程池中有一定數(shù)量的工作線程,工作線程會循環(huán)從任務(wù)隊列中獲取任務(wù)禁漓,并執(zhí)行這個任務(wù)。那么怎么去停止這些工作線程呢孵睬?這...
    wo883721閱讀 1,620評論 0 14
  • 不止是年少的時候吧,即使現(xiàn)在還是會無緣無故陷入進退兩難的困境秘狞,像這只可憐的小胖豬一樣叭莫,不知如何是好。
    悠悠我藍閱讀 438評論 4 3
  • 在回來的火車上烁试,輾轉(zhuǎn)反側(cè)雇初,已經(jīng)開始思慮回去后怎么保持聯(lián)系,要做哪些努力减响,想著為了要配上我心愛的姑娘靖诗,雄心壯...
    super6377閱讀 202評論 0 0
  • 利用下午閑暇之余,打開電腦統(tǒng)計分析所轄門店的本前半個月的銷量數(shù)據(jù)支示。通過分析對比得出刊橘,同樣的客單有的門店營業(yè)額卻...
    銘鑫小胖閱讀 256評論 1 4