今天繼續(xù)回到Java多線程的基礎(chǔ)問題上。今天主要想說一下:線程的狀態(tài)
在這里需要說一下,由于剛開始寫文章不久,總是想到什么就寫什么,可能就會出現(xiàn)基礎(chǔ)講講,跳到進階,然后又跑到基礎(chǔ),而且內(nèi)容里面的表達(dá)可能也不是很準(zhǔn)確,希望大家能夠原諒。我會慢慢改進,后面如果寫系列文章的話會先做一個整體的規(guī)劃。盡量不要像現(xiàn)在這么亂了。好了不說了,再說就變成水文了。
線程的狀態(tài)
開局一張圖,內(nèi)容全靠水。。。

- new(新建狀態(tài)):當(dāng)我們新new一個線程后,這個線程就是新建狀態(tài)
Thread t = new Thread() - ready(就緒狀態(tài)):new了一個線程,調(diào)用線程的start()方法后,這個線程將會進入就緒狀態(tài),這個時候他只是具備了可執(zhí)行的條件,但是還是沒有執(zhí)行
- running(執(zhí)行狀態(tài)):就緒狀態(tài)中的線程其實是被扔到了CPU的等待隊列排對等待翻牌子去了。等到CPU翻到了誰的牌子,誰就進入了執(zhí)行狀態(tài)。學(xué)過了yield之后我們都知道,調(diào)用它會讓執(zhí)行狀態(tài)的線程直接讓出當(dāng)前CPU跑到就緒狀態(tài)繼續(xù)排隊
- blocking(阻塞狀態(tài)):當(dāng)我們在線程中使用了synchronized關(guān)鍵字時,當(dāng)代碼運行到這里是需要獲取鎖之后才能繼續(xù)執(zhí)行,這種狀態(tài)叫做阻塞狀態(tài),由于阻塞時候CPU就會放棄你,所以當(dāng)我獲取到鎖之后我才會進入到就緒狀態(tài)再去排隊等待CPU翻牌子
- waitting(等待狀態(tài)):當(dāng)運行中的線程調(diào)用了wait(),join(),park()方法后,線程會等待其他線程執(zhí)行完成或者使用其他的方法來喚醒才能繼續(xù)進入執(zhí)行狀態(tài)
- timeWaitting(有時間等待狀態(tài)):當(dāng)運行中的線程調(diào)用了sleep(tiem)和調(diào)用上訴的方法帶上時間,線程會在那里等待直到時間結(jié)束就會自動進入執(zhí)行狀態(tài)
- over(結(jié)束狀態(tài)):當(dāng)一個線程正常執(zhí)行完成,或者上訴所有狀態(tài)運行過程中拋出異?;蛘{(diào)用interrupt方法都會讓線程進入到結(jié)束狀態(tài)。當(dāng)線程進入結(jié)束狀態(tài)后就無法再進行其他的狀態(tài)轉(zhuǎn)換,只有重新new才可以重新運行
我們可以通過簡單的小程序來看到部分的線程狀態(tài):
public class ThreadStateTest {
static class MyThread extends Thread {
@Override
public void run() {
try {
Thread.sleep(3000);
System.out.println("sleep 之后的狀態(tài):" + Thread.currentThread().getState());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
System.out.println("new 之后的狀態(tài):" + myThread.getState());
myThread.start();
System.out.println("start 之后的狀態(tài):" + myThread.getState());
myThread.join();//等待線程去死
System.out.println("join 之后的狀態(tài):" + myThread.getState());
}
}
注意
有一點需要注意,調(diào)用Lock的一些方法并不會讓線程進入blocked狀態(tài),而是會讓線程進入waitting狀態(tài)。
public class LockTests {
Lock lock = new ReentrantLock();
public static void main(String[] args) {
LockTests lockTest = new LockTests();
// 可重入鎖 + 基本使用 案例開始
for (int i = 0;i<3;i++) {
new Thread(()->{lockTest.tryLockTest();}, "線程 " + i).start();
}
}
public synchronized void tryLockTest(){
try {
try {
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
在他執(zhí)行的時候,我們使用jstack -l pid 命令就可以看到如下圖的樣子:
[圖片上傳失敗...(image-645e24-1698156369246)]
可以看到,沒有獲取到鎖的線程的狀態(tài)是blocked 的沒有問題,而且也看出了synchronized關(guān)鍵字實現(xiàn)鎖的方式是使用monitor
再看下面一個小程序:
public class LockTests {
Lock lock = new ReentrantLock();
public static void main(String[] args) {
LockTests lockTest = new LockTests();
// 可重入鎖 + 基本使用 案例開始
for (int i = 0;i<3;i++) {
new Thread(()->{lockTest.tryLockTest();}, "線程 " + i).start();
}
}
public void tryLockTest(){
try {
if (lock.tryLock(50, TimeUnit.SECONDS)) {
try {
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
由于我們使用的是tryLock(time) 所以沒有獲取到鎖的狀態(tài)是timed_waitting 可以看到他鎖定的方式是使用park,這個我準(zhǔn)備下一篇會說一下。他是locksupport工具類里面的方法。
總結(jié)
本篇文章總共講了線程的7中狀態(tài):new、runnable、running、blocking(blocked)、waitting、time_waitting、over(dead)
順便提了一個小的排查線程問題的工具:jstack