上一篇文章我們聊了多執行緒的基礎內容,比如為什麼要使用多執行緒,執行緒和程式之間的不同,以及建立執行緒的 4 種方式。本文已收錄至我的 Github: https://github.com/xiaoqi6666...
今天我們來說一下執行緒的生命週期和常用 APIs:我們需要非常清楚的知道執行緒的各種狀態,比如排查程式執行慢的原因時,就需要看下是不是哪裡被阻塞了;另外它也是面試時非常喜歡問的,如果基礎內容都答不好,恐怕直接就掛了。
本文分為兩大部分,
- 執行緒的 6 大狀態;
多執行緒常用的 APIs:
- join()
- wait()
- notify()
- yield()
- sleep()
- currentThread()
- getName()
- getId()
- getPriority()
- setPriority()
- stop()
執行緒狀態
關於執行緒的狀態,網上各種說法都有,比較流行的是 5 種或者 6 種。關於 5 種狀態的那個版本我沒有找到理論依據,如果有小夥伴清楚的也歡迎留言指出。
我這裡所寫的是根據 java.lang.Thread
的原始碼,執行緒有以下 6 大狀態:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITTING,
TIMED_WAITTING,
TERMINATED;
}
先上圖,我們再依次來看。
1. New
A thread that has not yet started is in this state.
就是指執行緒剛建立,還沒啟動的時候,比如剛 new
了一個 thread
。
MyThread myThread = new MyThread();
2. Runnable
A thread is executing in the Java virtual machine but it may be waiting for other resources from the operating system such as processor.
那麼接下來,自然就是要啟動執行緒了,也就是呼叫 thread
的 start()
方法。
myThread.start();
啟動之後,執行緒就進入了 Runnable
狀態。
此時所有的執行緒都會新增到一個等待佇列裡,等待“CPU 排程”。
如果搶佔到 CPU 的資源,那就執行;如果沒搶到,就等著唄,等當前正在執行的執行緒完成它能執行的時間片之後,再次搶佔。
要注意這裡在等待的一般是系統資源,而不是鎖或者其他阻塞。
3. Blocked
Thread state for a thread blocked waiting for a monitor lock.
A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method or reenter a synchronized block/method after callingwait()
Object.
這裡給出了非常明確的 use case
,就是被鎖在外面的才叫阻塞。所以這裡必須要有至少 2 個執行緒。
4. Waiting
A thread in the waiting state is waiting for another thread to perform a particular action.
那具體有哪些原因呢?
A thread is in the waiting state due to calling one of the following methods:
- Object.wait with no timeout
- Thread.join with no timeout
- LockSupport.park
所以說,當呼叫了
wait()
,join()
,park()
方法之後,執行緒進入等待狀態。
這裡的等待狀態是沒有時間限制的,可以無限的等下去... 所以需要有人來喚醒:
- 如果是通過
wait()
進入等待狀態的,需要有notify()
或者notifyAll()
方法來喚醒; - 如果是通過
join()
進入等待狀態的,需要等待目標執行緒執行結束。
比如在生產者消費者模型裡,當沒有商品的時候,消費者就需要等待,等待生產者生產好了商品發 notify()
。下一篇文章我們會細講。
5. Timed_waiting
導致這個狀態的原因如下:
- Thread.sleep
- Object.wait with timeout
- Thread.join with timeout
- LockSupport.parkNanos
- LockSupport.parkUntil
其實就是在上一種狀態的基礎上,給了具體的時間限制。
那麼當時間結束後,執行緒就解放了。
6. Terminated
A thread that has exited is in this state.
這裡有 3 種情況會終止執行緒:
- 執行完所有程式碼,正常結束;
- 強制被結束,比如呼叫了
stop()
方法,現在已經被棄用; - 丟擲了未捕獲的異常。
執行緒一旦死亡就不能復生。
如果在一個死去的執行緒上呼叫 start()
方法,那麼程式會丟擲 java.lang.IllegalThreadStateException
。
接下來我們說說多執行緒中常用的 11 個 APIs。
APIs
1. join()
join()
方法會強制讓該執行緒執行,並且一直會讓它執行完。
比如上一篇文章的例子是兩個執行緒交替執行的,那麼我們這裡該下,改成呼叫小齊執行緒.join()
,那麼效果就是先輸出 小齊666
。
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println("小齊666:" + i);
}
}
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new MyRunnable());
t.start();
t.join();
for(int i = 0; i < 100; i++) {
System.out.println("主執行緒" + i + ":齊姐666");
}
}
}
所以 join()
能夠保證某個執行緒優先執行,而且會一直讓它執行完,再回歸到公平競爭狀態。
join()
方法其實是用 wait()
來實現的,我們來看下這個方法。
2. wait() and notify()
wait()
其實並不是 Thread
類的方法,而是 Object
裡面的方法。
該方法就是讓當前物件等待,直到另一個物件呼叫 notify()
或者 notifyAll()
。
當然了,我們也可以設定一個等待時長,到時間之後物件將會自動甦醒。
4. yield()
yield
本身的中文意思是屈服,用在這裡倒也合適。
yield()
表示當前執行緒主動讓出 CPU 資源一下,然後我們再一起去搶。
注意這裡讓一下真的只是一下,從“執行中”回到“等待 CPU 分配資源”,然後所有執行緒再一起搶佔資源。
5. sleep()
顧名思義,這個方法就是讓當前執行緒睡一會,比如說,
myThread.sleep(1000); // 睡眠 1 秒鐘
它會丟擲一個 InterruptedException
異常,所以還要 try catch
一下。
6. currentThread()
Returns a reference to the currently executing thread object.
該方法是獲取當前執行緒物件。
注意它是一個 static
方法,所以直接通過 Thread
類呼叫。
比如列印當前執行緒
System.out.println(Thread.currentThread());
前文的例子中,它會輸出:
Thread[Thread-0,5,main]
Thread[main,5,main]
沒錯,它的返回值也是 Thread
型別。
7. getName()
該方法可以獲取當前執行緒名稱。
這個名稱可以自己設定,比如:
Thread t = new Thread(new MyRunnable(), "壹齊學");
8. getId()
該方法是獲取執行緒的 Id
.
9. getPriority()
執行緒也有優先順序的哦~
雖然優先順序高的執行緒並不能百分百保證一定會先執行,但它是有更大的概率被先執行的。
優先順序的範圍是 1-10
,我們來看原始碼:
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
如果不在這個範圍,JDK 丟擲 IllegalArgumentException()
的異常。
10. setPriority()
當然啦,我們也是可以自己設定某個執行緒的優先順序的。
設定的優先順序也需要在規定的 1-10
的範圍內哦,如果不在這個範圍也會拋異常。
11. stop()
最後我們來說下 stop()
方法,也是前文提到過的強制停止執行緒的一種方式,但現在已被棄用,因為會引起一些執行緒安全方面的問題。
好了,以上就是有關執行緒狀態和常用 API 的介紹了。相信大家看完之後對執行緒的整個流程應該有了清晰的認識,其實裡面還有很多細節我沒有展開,畢竟這是多執行緒的第 2 講,更深入的內容我們慢慢來。
如果你喜歡這篇文章,記得給我點贊留言哦~你們的支援和認可,就是我創作的最大動力,我們下篇文章見!
我是小齊,紐約程式媛,終生學習者,每天晚上 9 點,雲自習室裡不見不散!
更多幹貨文章見我的 Github: https://github.com/xiaoqi6666...**