Java併發程式設計:執行緒的生命週期是個怎樣的過程?
前言
在日常開發過程中,如果我們需要執行一些比較耗時的程式的話,一般來說都是開啟一個新執行緒,把耗時的程式碼放線上程裡,然後開啟執行緒執行。但執行緒是會耗費系統資源的,如果有多個執行緒同時執行,互相之間搶佔系統資源,那無疑會對系統造成極大的壓力。所以,怎麼操作執行緒,保證不影響整個應用功能是很重要的,而這就需要我們瞭解執行緒的生命週期了。
執行緒的生命週期
執行緒的生命週期有6種狀態,分別是NEW(新建)、RUNNABLE(可執行)、BLOCKED(被阻塞)、 WAITING(等待)、TIMED_WAITING(計時等待)、TERMINATED(被終止),在 Thread 原始碼的 State
public static enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
private State() {
}
}
1、NEW 狀態表示剛剛建立的執行緒,此時的執行緒還沒執行,也就是還沒執行start() 方法,建立執行緒的方式也比較簡單,可以參考《Java併發知識:Java建立執行緒的三種方式》。
2、當執行緒執行時,處於 RUNNABLE 狀態,表示執行緒所需的資源已經準備好了。
3、如果執行緒在執行的過程中遇到被阻塞的情況,例如執行緒中的程式中有synchronized 同步程式碼塊,執行緒就會暫停執行,進入阻塞狀態,直至獲取請求的鎖,這時執行緒就處於 BLOCKED
例項程式碼如下:
public class ThreadDemo { public static Object testObject = new Object(); public static class MyThread extends Thread { public MyThread(String name) { super.setName(name); } @Override public void run() { //每次跑run方法都需要獲取testObject物件 synchronized (testObject) { System.out.println("thread name:" + this.getName()); //..............耗時操作.............. } } } public static void main(String[] args) { for (int i = 1; i <= 100; i++) { new MyThread("執行緒"+i).start(); } } }
在上面的程式碼中,執行緒的run方法在執行耗時的程式之前都需要先獲取testobject物件的鎖,因為物件鎖是公共物件,所以,多個執行緒同時執行時,同一時刻只能有一個執行緒獲取鎖,假設某個時刻是 A執行緒 獲取了鎖,其他執行緒就會處於等待鎖釋放的阻塞狀態,直到獲取鎖才能繼續執行程式,這就是執行緒的BLOCKED 狀態。
4、WAITING 表示等待的狀態,處於 WAITING 狀態的執行緒會進入一個無時間限制的等待,一旦等到了期望的事件,執行緒就會再次執行,進入RUNNABLE 狀態。最典型的場景就是 等待(wait) 和 通知(notify)。
等待狀態對應的方法是wait(),而通知是notify(),這兩個方法並不屬於Thread類,而是屬於Object類,所以所有物件都可以使用這兩個方法。當一個物件例項 obj 呼叫 wait() 方法後,當前執行緒就會在這個物件上等待,直到其他執行緒呼叫 obj.notify() 為止。這時的物件例項 obj 就相當於多個執行緒之間的通訊工具。例項程式碼如下:
public class ThreadDemo {
public static Object testObject = new Object();
public static class MyThread1 extends Thread {
@Override
public void run() {
synchronized (testObject) {
System.out.println("MyThread1 wait :" + System.currentTimeMillis());
try {
//呼叫wait方法進入等待狀態
testObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static class MyThread2 extends Thread {
@Override
public void run() {
synchronized (testObject) {
System.out.println("MyThread2 start notify :" + System.currentTimeMillis());
//..............耗時操作..............
//發出通知,喚醒等待的執行緒
testObject.notify();
}
}
}
public static void main(String[] args) {
MyThread1 t1 = new MyThread1();
MyThread2 t2 = new MyThread2();
t1.start();
t2.start();
}
}
5、TIMED_WAITING 和 WAITING 一樣,都表示等待狀態,但TIMED_WAITING 會進行一個 有時限的等待。操作執行緒狀態有幾個方法是帶有超時引數的,呼叫方法的執行緒進入計時等待狀態。這一狀態將一直保持到超時期滿或者接收到適當的通知,最常見的應用就是呼叫 Thread.sleep() 方法。
例項程式碼如下:
public static class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread start :" + System.currentTimeMillis());
try {
//休眠兩秒鐘
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("MyThread end :" + System.currentTimeMillis());
}
}
public static void main(String[] args) {
MyThread t = new MyThread1();
t.start();
}
啟動執行緒後,程式執行到 Thread.sleep() 方法會處於休眠狀態,時間根據引數來決定,單位是毫秒,所以執行main方法後,後一條輸出內容會隔兩秒鐘出現。
MyThread start :1544704974271
MyThread end :1544704976272
6、當執行緒執行完畢後,進入 TERMINATED 狀態,表示結束,一般執行緒被終止有兩種原因:
run方法正常執行後就自然消亡。
因為一個沒有捕獲的異常終止了run方法而導致意外死亡。
好了,執行緒的生命週期就總結完了,用一張圖表示大概是這樣: