創(chuàng)新互聯(lián)是一家專業(yè)提供懷仁企業(yè)網(wǎng)站建設(shè),專注與網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站建設(shè)、H5網(wǎng)站設(shè)計(jì)、小程序制作等業(yè)務(wù)。10年已為懷仁眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)絡(luò)公司優(yōu)惠進(jìn)行中。
(手機(jī)橫屏看源碼更方便)
注:java源碼分析部分如無特殊說明均基于 java8 版本。
大家都知道線程是有生命周期,但是彤哥可以認(rèn)真負(fù)責(zé)地告訴你網(wǎng)上幾乎沒有一篇文章講得是完全正確的。
常見的錯(cuò)誤有:就緒狀態(tài)、運(yùn)行中狀態(tài)(RUNNING)、死亡狀態(tài)、中斷狀態(tài)、只有阻塞沒有等待狀態(tài)、流程圖亂畫等,最常見的錯(cuò)誤就是說線程只有5種狀態(tài)。
今天這篇文章會(huì)徹底講清楚線程的生命周期,并分析synchronized鎖、基于AQS的鎖中線程狀態(tài)變化的邏輯。
所以,對(duì)synchronized鎖和AQS原理(源碼)不了解的同學(xué),請(qǐng)翻一下彤哥之前的文章先熟悉這兩部分的內(nèi)容,否則肯定記不住這里講的線程生命周期。
(1)線程的狀態(tài)有哪些?
(2)synchronized鎖各階段線程處于什么狀態(tài)?
(3)重入鎖、條件鎖各階段線程處于什么狀態(tài)?
關(guān)于線程的生命周期,我們可以看一下java.lang.Thread.State
這個(gè)類,它是線程的內(nèi)部枚舉類,定義了線程的各種狀態(tài),并且注釋也很清晰。
public enum State {
/**
* 新建狀態(tài),線程還未開始
*/
NEW,
/**
* 可運(yùn)行狀態(tài),正在運(yùn)行或者在等待系統(tǒng)資源,比如CPU
*/
RUNNABLE,
/**
* 阻塞狀態(tài),在等待一個(gè)監(jiān)視器鎖(也就是我們常說的synchronized)
* 或者在調(diào)用了Object.wait()方法且被notify()之后也會(huì)進(jìn)入BLOCKED狀態(tài)
*/
BLOCKED,
/**
* 等待狀態(tài),在調(diào)用了以下方法后進(jìn)入此狀態(tài)
* 1. Object.wait()無超時(shí)的方法后且未被notify()前,如果被notify()了會(huì)進(jìn)入BLOCKED狀態(tài)
* 2. Thread.join()無超時(shí)的方法后
* 3. LockSupport.park()無超時(shí)的方法后
*/
WAITING,
/**
* 超時(shí)等待狀態(tài),在調(diào)用了以下方法后會(huì)進(jìn)入超時(shí)等待狀態(tài)
* 1. Thread.sleep()方法后【本文由公從號(hào)“彤哥讀源碼”原創(chuàng)】
* 2. Object.wait(timeout)方法后且未到超時(shí)時(shí)間前,如果達(dá)到超時(shí)了或被notify()了會(huì)進(jìn)入BLOCKED狀態(tài)
* 3. Thread.join(timeout)方法后
* 4. LockSupport.parkNanos(nanos)方法后
* 5. LockSupport.parkUntil(deadline)方法后
*/
TIMED_WAITING,
/**
* 終止?fàn)顟B(tài),線程已經(jīng)執(zhí)行完畢
*/
TERMINATED;
}
線程生命周期中各狀態(tài)的注釋完畢了,下面我們?cè)賮砜纯锤鳡顟B(tài)之間的流轉(zhuǎn):
怎么樣?是不是很復(fù)雜?彤哥幾乎把網(wǎng)上的資料都查了一遍,沒有一篇文章把這個(gè)流程圖完整畫出來的,下面彤哥就來一一解釋:
(1)為了方便講解,我們把鎖分成兩大類,一類是synchronized鎖,一類是基于AQS的鎖(我們拿重入鎖舉例),也就是內(nèi)部使用了LockSupport.park()/parkNanos()/parkUntil()幾個(gè)方法的鎖;
(2)不管是synchronized鎖還是基于AQS的鎖,內(nèi)部都是分成兩個(gè)隊(duì)列,一個(gè)是同步隊(duì)列(AQS的隊(duì)列),一個(gè)是等待隊(duì)列(Condition的隊(duì)列);
(3)對(duì)于內(nèi)部調(diào)用了object.wait()/wait(timeout)或者condition.await()/await(timeout)方法,線程都是先進(jìn)入等待隊(duì)列,被notify()/signal()或者超時(shí)后,才會(huì)進(jìn)入同步隊(duì)列;
(4)明確聲明,BLOCKED狀態(tài)只有線程處于synchronized的同步隊(duì)列的時(shí)候才會(huì)有這個(gè)狀態(tài),其它任何情況都跟這個(gè)狀態(tài)無關(guān);
(5)對(duì)于synchronized,線程執(zhí)行synchronized的時(shí)候,如果立即獲得了鎖(沒有進(jìn)入同步隊(duì)列),線程處于RUNNABLE狀態(tài);
(6)對(duì)于synchronized,線程執(zhí)行synchronized的時(shí)候,如果無法獲得鎖(直接進(jìn)入同步隊(duì)列),線程處于BLOCKED狀態(tài);
(5)對(duì)于synchronized內(nèi)部,調(diào)用了object.wait()之后線程處于WAITING狀態(tài)(進(jìn)入等待隊(duì)列);
(6)對(duì)于synchronized內(nèi)部,調(diào)用了object.wait(timeout)之后線程處于TIMED_WAITING狀態(tài)(進(jìn)入等待隊(duì)列);
(7)對(duì)于synchronized內(nèi)部,調(diào)用了object.wait()之后且被notify()了,如果線程立即獲得了鎖(也就是沒有進(jìn)入同步隊(duì)列),線程處于RUNNABLE狀態(tài);
(8)對(duì)于synchronized內(nèi)部,調(diào)用了object.wait(timeout)之后且被notify()了,如果線程立即獲得了鎖(也就是沒有進(jìn)入同步隊(duì)列),線程處于RUNNABLE狀態(tài);
(9)對(duì)于synchronized內(nèi)部,調(diào)用了object.wait(timeout)之后且超時(shí)了,這時(shí)如果線程正好立即獲得了鎖(也就是沒有進(jìn)入同步隊(duì)列),線程處于RUNNABLE狀態(tài);
(10)對(duì)于synchronized內(nèi)部,調(diào)用了object.wait()之后且被notify()了,如果線程無法獲得鎖(也就是進(jìn)入了同步隊(duì)列),線程處于BLOCKED狀態(tài);
(11)對(duì)于synchronized內(nèi)部,調(diào)用了object.wait(timeout)之后且被notify()了或者超時(shí)了,如果線程無法獲得鎖(也就是進(jìn)入了同步隊(duì)列),線程處于BLOCKED狀態(tài);
(12)對(duì)于重入鎖,線程執(zhí)行l(wèi)ock.lock()的時(shí)候,如果立即獲得了鎖(沒有進(jìn)入同步隊(duì)列),線程處于RUNNABLE狀態(tài);
(13)對(duì)于重入鎖,線程執(zhí)行l(wèi)ock.lock()的時(shí)候,如果無法獲得鎖(直接進(jìn)入同步隊(duì)列),線程處于WAITING狀態(tài);
(14)對(duì)于重入鎖內(nèi)部,調(diào)用了condition.await()之后線程處于WAITING狀態(tài)(進(jìn)入等待隊(duì)列);
(15)對(duì)于重入鎖內(nèi)部,調(diào)用了condition.await(timeout)之后線程處于TIMED_WAITING狀態(tài)(進(jìn)入等待隊(duì)列);
(16)對(duì)于重入鎖內(nèi)部,調(diào)用了condition.await()之后且被signal()了,如果線程立即獲得了鎖(也就是沒有進(jìn)入同步隊(duì)列),線程處于RUNNABLE狀態(tài);
(17)對(duì)于重入鎖內(nèi)部,調(diào)用了condition.await(timeout)之后且被signal()了,如果線程立即獲得了鎖(也就是沒有進(jìn)入同步隊(duì)列),線程處于RUNNABLE狀態(tài);
(18)對(duì)于重入鎖內(nèi)部,調(diào)用了condition.await(timeout)之后且超時(shí)了,這時(shí)如果線程正好立即獲得了鎖(也就是沒有進(jìn)入同步隊(duì)列),線程處于RUNNABLE狀態(tài);
(19)對(duì)于重入鎖內(nèi)部,調(diào)用了condition.await()之后且被signal()了,如果線程無法獲得鎖(也就是進(jìn)入了同步隊(duì)列),線程處于WAITING狀態(tài);
(20)對(duì)于重入鎖內(nèi)部,調(diào)用了condition.await(timeout)之后且被signal()了或者超時(shí)了,如果線程無法獲得鎖(也就是進(jìn)入了同步隊(duì)列),線程處于WAITING狀態(tài);
(21)對(duì)于重入鎖,如果內(nèi)部調(diào)用了condition.await()之后且被signal()之后依然無法獲取鎖的,其實(shí)經(jīng)歷了兩次WAITING狀態(tài)的切換,一次是在等待隊(duì)列,一次是在同步隊(duì)列;
(22)對(duì)于重入鎖,如果內(nèi)部調(diào)用了condition.await(timeout)之后且被signal()或超時(shí)了的,狀態(tài)會(huì)有一個(gè)從TIMED_WAITING切換到WAITING的過程,也就是從等待隊(duì)列進(jìn)入到同步隊(duì)列;
為了便于理解,彤哥這里每一條都分的比較細(xì),麻煩耐心看完。
看完上面的部分,你肯定想知道怎么去驗(yàn)證,下面彤哥就說說驗(yàn)證的方法,先給出測(cè)試用例。
public class ThreadLifeTest {
public static void main(String[] args) throws IOException {
Object object = new Object();
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(()->{
synchronized (object) {
try {
System.out.println("thread1 waiting");
object.wait();
// object.wait(5000);
System.out.println("thread1 after waiting");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Thread1").start();
new Thread(()->{
synchronized (object) {
try {
System.out.println("thread2 notify");
// 打開或關(guān)閉這段注釋,觀察Thread1的狀態(tài)
// object.notify();【本文由公從號(hào)“彤哥讀源碼”原創(chuàng)】
// notify之后當(dāng)前線程并不會(huì)釋放鎖,只是被notify的線程從等待隊(duì)列進(jìn)入同步隊(duì)列
// sleep也不會(huì)釋放鎖
Thread.sleep(10000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Thread2").start();
new Thread(()->{
lock.lock();
System.out.println("thread3 waiting");
try {
condition.await();
// condition.await(200, (TimeUnit).SECONDS);
System.out.println("thread3 after waiting");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "Thread3").start();
new Thread(()->{
lock.lock();
System.out.println("thread4");
// 打開或關(guān)閉這段注釋,觀察Thread3的狀態(tài)
// condition.signal();【本文由公從號(hào)“彤哥讀源碼”原創(chuàng)】
// signal之后當(dāng)前線程并不會(huì)釋放鎖,只是被signal的線程從等待隊(duì)列進(jìn)入同步隊(duì)列
// sleep也不會(huì)釋放鎖
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "Thread4").start();
}
}
打開或關(guān)閉上面注釋部分的代碼,使用IDEA的RUN模式運(yùn)行代碼,然后點(diǎn)擊左邊的一個(gè)攝像頭按鈕(jstack),查看各線程的狀態(tài)。
注:不要使用DEBUG模式,DEBUG模式全都變成WAITING狀態(tài)了,很神奇。
其實(shí),本來這篇是準(zhǔn)備寫線程池的生命周期的,奈何線程的生命周期寫了太多,等下一篇我們?cè)賮硪黄饘W(xué)習(xí)線程池的生命周期吧。