java多執行緒狀態詳解
java.lang.Thread類中有個內部列舉類State用來描述執行緒的各種狀態,具體如下
public enum State {
/**
* 尚未啟動的執行緒的狀態。
*/
NEW,
/**
* 可執行執行緒的執行緒狀態。處於可執行狀態的某一執行緒正在Java虛擬機器中執行,但它可能正在等待作業系統 * 中的其他資源,比如處理器。
*/
RUNNABLE,
/**
* 受阻塞並且正在等待監視器鎖的某一執行緒的執行緒狀態。處於受阻塞狀態的某一執行緒正在等待監視器鎖,以 * 便進入一個同步程式碼塊/方法,或者在呼叫Object.wait之後再次進入同步程式碼塊/方法。
*/
BLOCKED,
/**
* 某一等待執行緒的執行緒狀態。某一執行緒因為呼叫下列方法之一而處於等待狀態:
* <ul>
* <li>不帶超時值的Object.wait</li>
* <li>不帶超時值的Thread.join</li>
* <li>LockSupport.park<li>
* </ul>
* 處於等待狀態的執行緒正等待另一個執行緒執行特定操作。 例如,已經在某一物件上呼叫了Object.wait()的 * 執行緒正等待另一個執行緒在該物件上呼叫Object.notify()或Object.notifyAll()。已經呼叫了 * Thread.join()的執行緒正在等待指定執行緒終止。
*/
WAITING,
/**
* 具有指定等待時間的某一等待執行緒的執行緒狀態。某一執行緒因為呼叫以下帶有指定正等待時間的方法之一而處 * 於定時等待狀態:
* <ul>
* <li>Thread.sleep</li>
* <li>帶有超時值的 Object.wait</li>
* <li>帶有超時值的 Thread.join</li>
* <li>LockSupport.parkNanos</li>
* <li>LockSupport.parkUntil</li>
* </ul>
*/
TIMED_WAITING,
/**
* 已終止執行緒的執行緒狀態。執行緒已經結束執行。
*/
TERMINATED;
}
下面就用程式碼呈現各種執行緒狀態
NEW狀態
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) { Thread t = new Thread(); System.out.println(t.getState()); } }
輸出結果就是
NEW
RUNNABLE狀態
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) { Thread t = new Thread(); t.start(); System.out.println(t.getState()); } }
輸出結果就是
RUNNABLE
,不過這樣並不是每次都能打印出RUNNABLE,也有可能打印出TERMINATED,因為main執行緒和t執行緒是併發執行的,有可能在列印執行緒狀態時t執行緒已經結束運行了。為了總能打印出RUNNABLE,讓t執行緒無限執行下去,程式碼如下package cn.cjc.multithread; public class MainTest { public static void main(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { while (true) ;//無限迴圈 } }); t.start(); System.out.println(t.getState()); } }
還有一種檢視執行緒狀態的方法,就是用JVM命令檢視,對於上面的程式碼,程式會一直執行,所以可以用jstack命令dump出線程狀態,操作如下
1)用jps命令檢視JVM程序的pid,可知pid=4069
2)用jstack命令列印執行緒棧資訊,可以看到Thread-0的執行緒處於RUNNABLE狀態需要注意的是執行緒狀態是RUNNABLE不代表該執行緒正在執行中,有可能該執行緒時間片用完,正在等待CPU,也就是處於就緒狀態。所以RUNNABLE有可能是正在執行中,也有可能是就緒狀態。
BLOCKED狀態
執行緒呼叫synchronized方法或者進入synchronized程式碼塊時,獲取不到監視器鎖而阻塞,則產生該狀態
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) { final MainTest mainTest = new MainTest(); new Thread(new Runnable() { @Override public void run() { mainTest.m1(); } }, "thread-m1").start(); new Thread(new Runnable() { @Override public void run() { mainTest.m2(); } }, "thread-m2").start(); } public synchronized void m1() { System.out.println("m1"); while (true) ; } public synchronized void m2() { System.out.println("m2"); while (true) ; } }
執行後用jstack命令檢視執行緒狀態發現thread-m1執行緒是RUNNABLE狀態,而thread-m2執行緒是BLOCKED (on object monitor)狀態,因為同步鎖一直被thread-m1持有,thread-m2在等待鎖
還有一種情況也會產生BLOCKED狀態,程式碼如下
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) throws InterruptedException { final MainTest mainTest = new MainTest(); new Thread(new Runnable() { @Override public void run() { mainTest.m1(); } }, "thread-m1").start(); new Thread(new Runnable() { @Override public void run() { mainTest.m2(); } }, "thread-m2").start(); Thread.sleep(3000); new Thread(new Runnable() { @Override public void run() { mainTest.m3(); } }, "thread-m3").start(); } public synchronized void m1() { System.out.println("m1---start"); try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m1---end"); while (true) ; } public synchronized void m2() { System.out.println("m2---start"); try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m2---end"); while (true) ; } public synchronized void m3() { System.out.println("m3"); this.notifyAll();//注意不是this.notify()哦 } }
執行後用jstack命令列印如下
發現thread-m1執行緒是BLOCKED (on object monitor)狀態,這和第一種情況不是一樣嗎?其實不一樣,看此處thread-m1執行緒棧的第一行有個in Object.wait(),而第一種情況thread-m2執行緒棧的第一行是waiting for monitor entry,有啥區別?
第二種情況產生過程是這樣:m1執行緒先持有鎖,然後釋放鎖並等待被喚醒,緊接著m2執行緒持有鎖,然後釋放鎖並等待被喚醒,m3執行緒呼叫notifyAll方法喚醒了所有等待著的執行緒,這時候m2執行緒拿到鎖並一直持有,m1執行緒因為被喚醒了,但沒有拿到鎖,所以從WAITING狀態變成了BLOCKED狀態。
第一種情況下執行緒BLOCKED,是在Entry Set裡面,第二種情況執行緒BLOCKED,是在Wait Set裡面。
我要說還有第三種情況的BLOCKED,會不會被打?真有呢!就是呼叫帶超時時間的Object.wait且看程式碼
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) { final MainTest mainTest = new MainTest(); new Thread(new Runnable() { @Override public void run() { mainTest.m1(); } }, "thread-m1").start(); new Thread(new Runnable() { @Override public void run() { mainTest.m2(); } }, "thread-m2").start(); } public synchronized void m1() { System.out.println("m1---start"); try { this.wait(30000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m1---end"); while (true) ; } public synchronized void m2() { System.out.println("m2---start"); try { this.wait(30000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m2---end"); while (true) ; } }
執行起來後,30s內用jstack命令檢視執行緒m1和m2的狀態都是WAITING (on object monitor),但是30s以後檢視執行緒狀態,如圖示
thread-m2執行緒處在BLOCKED狀態,但是阻塞原因第二情況是in Object.wait(),而這個是waiting for monitor entry,我以為會和第二種情況一樣(為什麼會不一樣呢?後面有時間再深入看看),納尼?這不是和第一種進入synchronized同步塊的情況一樣嗎?確實很像,但是從阻塞執行緒棧的第三行往下看就可以區分開來了。
WAITING狀態
WAITING狀態線上程棧中有兩種不同的體現,分別為WAITING (on object monitor)和WAITING (parking)。
1)呼叫Object.wait()產生WAITING (on object monitor)狀態,程式碼如下
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) { final MainTest mainTest = new MainTest(); new Thread(new Runnable() { @Override public void run() { mainTest.m1(); } }, "thread-m1").start(); } public synchronized void m1() { System.out.println("m1---start"); try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m1---end"); } }
執行後的執行緒棧如下
2)呼叫Thread.join產生WAITING (on object monitor)狀態,程式碼如下
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(new Runnable() { @Override public void run() { while (true) ; } }); t.start(); t.join();//等待t執行緒結束後再往下執行 System.out.println("main---end"); } }
執行後執行緒棧如下
3)呼叫java.util.concurrent.locks.Lock#lock方法會產生WAITING (parking)狀態,程式碼如下
package cn.cjc.multithread; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class MainTest { private Lock lock = new ReentrantLock(); public static void main(String[] args) { final MainTest mainTest = new MainTest(); new Thread(new Runnable() { @Override public void run() { mainTest.m1(); } }, "thread-m1").start(); new Thread(new Runnable() { @Override public void run() { mainTest.m2(); } }, "thread-m2").start(); } public void m1() { lock.lock(); try { System.out.println("m1---start"); while (true) ; } finally { lock.unlock(); System.out.println("m1---end"); } } public void m2() { lock.lock(); try { System.out.println("m2---start"); while (true) ; } finally { lock.unlock(); System.out.println("m2---end"); } } }
執行後的執行緒棧如下
可以發現thread-m1執行緒在執行中,而thread-m2執行緒沒有獲得鎖處於WAITING狀態,這個和進入synchronized方法或程式碼塊時沒有獲得鎖產生的狀態是完全不一樣的。
另外java.util.concurrent.locks.Condition#await()方法也會產生WAITING (parking)狀態,
java.util.concurrent.BlockingQueue#put
和java.util.concurrent.BlockingQueue#take
底層呼叫的就是此方法。其實它們底層呼叫的都是LockSupport.park方法。TIMED_WAITING狀態
TIMED_WAITING狀態要細分的話,會有三種,分別是TIMED_WAITING (sleeping)、TIMED_WAITING (on object monitor)、TIMED_WAITING (parking)
1)Thread.sleep方法會產生第一種狀態;
2)帶超時時間的Object.wait和帶超時時間的Thread.join方法會產生第二種狀態;
3)帶超時時間的lock.tryLock和帶超時時間的Condition.await方法會產生第三種狀態,帶超時時間的
java.util.concurrent.BlockingQueue#offer
和java.util.concurrent.BlockingQueue#poll
的底層呼叫的就是帶超時時間的Condition.awaitNanos方法。TERMINATED狀態
這個狀態最好理解,如程式碼
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(); t.start(); Thread.sleep(3000); System.out.println(t.getState()); } }
輸出結果就是
TERMINATED