日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java线程池深入讲解_死磕 java线程系列之线程池深入解析——生命周期

發布時間:2025/3/15 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java线程池深入讲解_死磕 java线程系列之线程池深入解析——生命周期 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

(手機橫屏看源碼更方便)

注:java源碼分析部分如無特殊說明均基于 java8 版本。

注:線程池源碼部分如無特殊說明均指ThreadPoolExecutor類。

簡介

上一章我們一起重溫了下線程的生命周期(六種狀態還記得不?),但是你知不知道其實線程池也是有生命周期的呢?!

問題

(1)線程池的狀態有哪些?

(2)各種狀態下對于任務隊列中的任務有何影響?

先上源碼

其實,在我們講線程池體系結構的時候,講了一些方法,比如shutDown()/shutDownNow(),它們都是與線程池的生命周期相關聯的。

我們先來看一下線程池ThreadPoolExecutor中定義的生命周期中的狀態及相關方法:

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

private static final int COUNT_BITS = Integer.SIZE - 3; // =29

private static final int CAPACITY = (1 << COUNT_BITS) - 1; // =000 11111...

// runState is stored in the high-order bits

private static final int RUNNING = -1 << COUNT_BITS; // 111 00000...

private static final int SHUTDOWN = 0 << COUNT_BITS; // 000 00000...

private static final int STOP = 1 << COUNT_BITS; // 001 00000...

private static final int TIDYING = 2 << COUNT_BITS; // 010 00000...

private static final int TERMINATED = 3 << COUNT_BITS; // 011 00000...

// 線程池的狀態

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

// 線程池中工作線程的數量

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

// 計算ctl的值,等于運行狀態“加上”線程數量

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

從上面這段代碼,我們可以得出:

(1)線程池的狀態和工作線程的數量共同保存在控制變量ctl中,類似于AQS中的state變量,不過這里是直接使用的AtomicInteger,這里換成unsafe+volatile也是可以的;

(2)ctl的高三位保存運行狀態,低29位保存工作線程的數量,也就是說線程的數量最多只能有(2^29-1)個,也就是上面的CAPACITY;

(3)線程池的狀態一共有五種,分別是RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED;

(4)RUNNING,表示可接受新任務,且可執行隊列中的任務;

(5)SHUTDOWN,表示不接受新任務,但可執行隊列中的任務;

(6)STOP,表示不接受新任務,且不再執行隊列中的任務,且中斷正在執行的任務;

(7)TIDYING,所有任務已經中止,且工作線程數量為0,最后變遷到這個狀態的線程將要執行terminated()鉤子方法,只會有一個線程執行這個方法;

(8)TERMINATED,中止狀態,已經執行完terminated()鉤子方法;

流程圖

下面我們再來看看這些狀態之間是怎么流轉的:

(1)新建線程池時,它的初始狀態為RUNNING,這個在上面定義ctl的時候可以看到;

(2)RUNNING->SHUTDOWN,執行shutdown()方法時;

(3)RUNNING->STOP,執行shutdownNow()方法時;

(4)SHUTDOWN->STOP,執行shutdownNow()方法時【本文由公從號“彤哥讀源碼”原創】;

(5)STOP->TIDYING,執行了shutdown()或者shutdownNow()后,所有任務已中止,且工作線程數量為0時,此時會執行terminated()方法;

(6)TIDYING->TERMINATED,執行完terminated()方法后;

源碼分析

你以為貼個狀態的源碼,畫個圖就結束了嘛?那肯定不能啊,下面讓我們一起來看看源碼中是怎么控制的。

(1)RUNNING

RUNNING,比較簡單,創建線程池的時候就會初始化ctl,而ctl初始化為RUNNING狀態,所以線程池的初始狀態就為RUNNING狀態。

// 初始狀態為RUNNING

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

(2)SHUTDOWN

執行shutdown()方法時把狀態修改為SHUTDOWN,這里肯定會成功,因為advanceRunState()方法中是個自旋,不成功不會退出。

public void shutdown() {

final ReentrantLock mainLock = this.mainLock;

mainLock.lock();

try {

checkShutdownAccess();

// 修改狀態為SHUTDOWN

advanceRunState(SHUTDOWN);

// 標記空閑線程為中斷狀態

interruptIdleWorkers();

onShutdown();

} finally {

mainLock.unlock();

}

tryTerminate();

}

private void advanceRunState(int targetState) {

for (;;) {

int c = ctl.get();

// 如果狀態大于SHUTDOWN,或者修改為SHUTDOWN成功了,才會break跳出自旋

if (runStateAtLeast(c, targetState) ||

ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))

break;

}

}

(3)STOP

執行shutdownNow()方法時,會把線程池狀態修改為STOP狀態,同時標記所有線程為中斷狀態。

public List shutdownNow() {

List tasks;

final ReentrantLock mainLock = this.mainLock;

mainLock.lock();

try {

checkShutdownAccess();

// 修改為STOP狀態

advanceRunState(STOP);

// 標記所有線程為中斷狀態

interruptWorkers();

tasks = drainQueue();

} finally {

// 【本文由公從號“彤哥讀源碼”原創】

mainLock.unlock();

}

tryTerminate();

return tasks;

}

至于線程是否響應中斷其實是在隊列的take()或poll()方法中響應的,最后會到AQS中,它們檢測到線程中斷了會拋出一個InterruptedException異常,然后getTask()中捕獲這個異常,并且在下一次的自旋時退出當前線程并減少工作線程的數量。

private Runnable getTask() {

boolean timedOut = false; // Did the last poll() time out?

for (;;) {

int c = ctl.get();

int rs = runStateOf(c);

// 如果狀態為STOP了,這里會直接退出循環,且減少工作線程數量

// 退出循環了也就相當于這個線程的生命周期結束了

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 {

// 真正響應中斷是在poll()方法或者take()方法中

Runnable r = timed ?

workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :

workQueue.take();

if (r != null)

return r;

timedOut = true;

} catch (InterruptedException retry) {

// 這里捕獲中斷異常

timedOut = false;

}

}

}

這里有一個問題,就是已經通過getTask()取出來且返回的任務怎么辦?

實際上它們會正常執行完畢,有興趣的同學可以自己看看runWorker()這個方法,我們下一節會分析這個方法。

(4)TIDYING

當執行shutdown()或shutdownNow()之后,如果所有任務已中止,且工作線程數量為0,就會進入這個狀態。

final void tryTerminate() {

for (;;) {

int c = ctl.get();

// 下面幾種情況不會執行后續代碼

// 1. 運行中

// 2. 狀態的值比TIDYING還大,也就是TERMINATED

// 3. SHUTDOWN狀態且任務隊列不為空

if (isRunning(c) ||

runStateAtLeast(c, TIDYING) ||

(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))

return;

// 工作線程數量不為0,也不會執行后續代碼

if (workerCountOf(c) != 0) {

// 嘗試中斷空閑的線程

interruptIdleWorkers(ONLY_ONE);

return;

}

final ReentrantLock mainLock = this.mainLock;

mainLock.lock();

try {

// CAS修改狀態為TIDYING狀態

if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {

try {

// 更新成功,執行terminated鉤子方法

terminated();

} finally {

// 強制更新狀態為TERMINATED,這里不需要CAS了

ctl.set(ctlOf(TERMINATED, 0));

termination.signalAll();

}

return;

}

} finally {

mainLock.unlock();

}

// else retry on failed CAS

}

}

實際更新狀態為TIDYING和TERMINATED狀態的代碼都在tryTerminate()方法中,實際上tryTerminated()方法在很多地方都有調用,比如shutdown()、shutdownNow()、線程退出時,所以說幾乎每個線程最后消亡的時候都會調用tryTerminate()方法,但最后只會有一個線程真正執行到修改狀態為TIDYING的地方。

修改狀態為TIDYING后執行terminated()方法,最后修改狀態為TERMINATED,標志著線程池真正消亡了。

(5)TERMINATED

見TIDYING中分析。

彩蛋

本章我們一起從狀態定義、流程圖、源碼分析等多個角度一起學習了線程池的生命周期,你掌握的怎么樣呢?

下一章我們將開始學習線程池執行任務的主流程,對這一塊內容感到恐懼的同學可以先看看彤哥之前寫的“手寫線程池”的兩篇文章,對接下來學習線程池的主要流程非常有好處。

歡迎關注我的公眾號“彤哥讀源碼”,查看更多源碼系列文章, 與彤哥一起暢游源碼的海洋。

總結

以上是生活随笔為你收集整理的java线程池深入讲解_死磕 java线程系列之线程池深入解析——生命周期的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。