轉 Java執行緒的6種狀態分析
https://blog.csdn.net/u010285974/article/details/107614693
想起來寫一下Java執行緒狀態,還是源起與最近的一次問題定位,當時碰到一個偶先超時的問題,使用jstack命令打印出堆疊資訊之後,例如
"transport-vert.x-eventloop-thread-11" #37 prio=5 os_prio=0 tid=0x00007f628d0f8800 nid=0x2a32 runnable [0x00007f62631f8000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
- locked <0x00000006f3cb4310> (a io.netty.channel.nio.SelectedSelectionKeySet)
- locked <0x00000006f3cb42c8> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:62)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:410)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
"soul-netty-nio-4" #72 daemon prio=5 os_prio=0 tid=0x00000000024d0000 nid=0x579f runnable [0x00007fdecf301000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
- locked <0x000000008130eed8> (a io.netty.channel.nio.SelectedSelectionKeySet)
- locked <0x000000008130ffd8> (a java.util.Collections$UnmodifiableSet)
- locked <0x000000008130ff00> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:62)
at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:757)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:412)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:897)
at java.lang.Thread.run(Thread.java:748)
通過定位執行緒的狀態,找到了錯誤的原因,也稍微費了些勁,所以想到把執行緒的一些狀態再這裡面總結一下
執行緒的6種狀態
Java執行緒Thread
在package java.lang;
中可以找到,通過原始碼,我們可以看到其狀態有如下6種
- NEW
- RUNNABLE
- BLOCKED
- WAITING
- TIMED_WAITING
- TERMINATED
下面分別解釋一下各種狀態
NEW
顧名思義,這個狀態,只存在於執行緒剛建立,未start
之前,例如
- MyThread thread = new MyThread();
- System.out.println(thread.getState());
此時打印出來的狀態就是NEW
RUNNABLE
這個狀態的執行緒,其正在JVM中執行,但是這個"執行",不一定是真的在執行, 也有可能是在等待CPU資源。所以,在網上,有人把這個狀態區分為READY和RUNNING兩個,一個表示的start了,資源一到位隨時可以執行,另一個表示真正的執行中,例如
- MyThread thread = new MyThread(lock);
- thread.start();
- System.out.println(thread.getState());
BLOCKED
這個狀態,一般是執行緒等待獲取一個鎖,來繼續執行下一步的操作,比較經典的就是synchronized
關鍵字,這個關鍵字修飾的程式碼塊或者方法,均需要獲取到對應的鎖,在未獲取之前,其執行緒的狀態就一直未BLOCKED,如果執行緒長時間處於這種狀態下,我們就是當心看是否出現死鎖的問題了。例如
- public class MyThread extends Thread {
- private byte[] lock = new byte[0];
- public MyThread(byte[] lock) {
- this.lock = lock;
- }
- @Override
- public void run() {
- synchronized (lock){
- try {
- Thread.sleep(10000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("done");
- }
- }
- }
- public static void main(String[] args) throws InterruptedException {
- byte[] lock = new byte[0];
- MyThread thread1 = new MyThread(lock);
- thread1.start();
- MyThread thread2 = new MyThread(lock);
- thread2.start();
- Thread.sleep(1000);//等一會再檢查狀態
- System.out.println(thread2.getState());
- }
此時我們看到的輸出的第二個執行緒的狀態就是BLOCKED
WAITING
一個執行緒會進入這個狀態,一定是執行了如下的一些程式碼,例如
- Object.wait()
- Thread.join()
- LockSupport.park()
當一個執行緒執行了Object.wait()的時候,它一定在等待另一個執行緒執行Object.notify()或者Object.notifyAll()。
或者一個執行緒thread,其在主執行緒中被執行了thread.join()的時候,主執行緒即會等待該執行緒執行完成。當一個執行緒執行了LockSupport.park()的時候,其在等待執行LockSupport.unpark(thread)。當該執行緒處於這種等待的時候,其狀態即為WAITING。需要關注的是,這邊的等待是沒有時間限制的,當發現有這種狀態的執行緒的時候,若其長時間處於這種狀態,也需要關注下程式內部有無邏輯異常。例如
LockSupport.park()
- public class MyThread extends Thread {
- private byte[] lock = new byte[0];
- public MyThread(byte[] lock) {
- this.lock = lock;
- }
- @Override
- public void run() {
- LockSupport.park();
- }
- }
- public static void main(String[] args) throws InterruptedException {
- byte[] lock = new byte[0];
- MyThread thread1 = new MyThread(lock);
- thread1.start();
- Thread.sleep(100);
- System.out.println(thread1.getState());
- LockSupport.unpark(thread1);
- Thread.sleep(100);
- System.out.println(thread1.getState());
- }
輸出WAITING和TERMINATED
Object.wait()
- public class MyThread extends Thread {
- private byte[] lock = new byte[0];
- public MyThread(byte[] lock) {
- this.lock = lock;
- }
- @Override
- public void run() {
- synchronized (lock){
- try {
- lock.wait(); //wait並允許其他執行緒同步lock
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public static void main(String[] args)
- throws InterruptedException {
- byte[] lock = new byte[0];
- MyThread thread1 = new MyThread(lock);
- thread1.start();
- Thread.sleep(100);
- System.out.println(thread1.getState()); //這時候執行緒狀態應為WAITING
- synchronized (lock){
- lock.notify(); //notify通知wait的執行緒
- }
- Thread.sleep(100);
- System.out.println(thread1.getState());
- }
輸出WAITING和TERMINATED
Thread.join()
- public class MyThread extends Thread {
- private byte[] lock = new byte[0];
- public MyThread(byte[] lock) {
- this.lock = lock;
- }
- @Override
- public void run() {
- try {
- Thread.sleep(10000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- public class MyThread1 extends Thread {
- Thread thread;
- public MyThread1(Thread thread) {
- this.thread = thread;
- }
- public void run() {
- try {
- thread.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- public class Main {
- public static void main(String[] args)
- throws InterruptedException {
- byte[] lock = new byte[0];
- MyThread thread = new MyThread(lock);
- thread.start();
- MyThread1 thread1 = new MyThread1(thread);
- thread1.start();
- Thread.sleep(100);
- System.out.println(thread1.getState());
- }
- }
輸出為WAITING
TIMED_WAITING
這個狀態和WAITING狀態的區別就是,這個狀態的等待是有一定時效的,即可以理解為WAITING狀態等待的時間是永久的,即必須等到某個條件符合才能繼續往下走,否則執行緒不會被喚醒。但是TIMED_WAITING,等待一段時間之後,會喚醒執行緒去重新獲取鎖。當執行如下程式碼的時候,對應的執行緒會進入到TIMED_WAITING狀態
- Thread.sleep(long)
- Object.wait(long)
- Thread.join(long)
- LockSupport.parkNanos()
- LockSupport.parkUntil()
程式碼示例
Thread.sleep
- public class MyThread3 extends Thread {
- public void run() {
- try {
- Thread.sleep(10000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- Thread thread = new MyThread3();
- thread.start();
- Thread.sleep(100);
- System.out.println(thread.getState());
輸出為TIMED_WAITING
Object.wait
- public class MyThread4 extends Thread {
- private Object lock;
- public MyThread4(Object lock) {
- this.lock = lock;
- }
- @Override
- public void run() {
- synchronized (lock){
- try {
- lock.wait(1000);//注意,此處1s之後執行緒醒來,會重新嘗試去獲取鎖,如果拿不到,後面的程式碼也不執行
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("lock end");
- }
- }
- }
- byte[] lock = new byte[0];
- MyThread4 thread = new MyThread4(lock);
- thread.start();
- Thread.sleep(100);
- System.out.println(thread.getState());
- Thread.sleep(2000);
- System.out.println(thread.getState());
輸出
TIMED_WAITING
lock end
TERMINATED
其餘方法類似
TERMINATED
這個狀態很好理解,即為執行緒執行結束之後的狀態
狀態之間的切換
執行緒狀態切換.png
以上這張圖,能夠較好的說明執行緒之前的狀態切換