java中,線程的狀態使用一個枚舉類型來描述的。這個枚舉一共有6個值: NEW(新建)、RUNNABLE(運行)、BLOCKED(鎖池)、TIMED_WAITING(定時等待)、WAITING(等待)、TERMINATED(終止、結束)。html
可是我發現大多數人的理解和上面的這六種仍是有些差異,一般會加上阻塞狀態,可運行狀態,掛起狀態。java
這是Thread類描述線程狀態的枚舉類的源代碼:ide
public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, /** * 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 calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }
1、大多數人對線程狀態以及狀態轉換的理解this
線程的狀態轉換:spa
當一個線程建立之後,就處於新建狀態。那何時這個狀態會改變呢?只要它調用的start()方法,線程就進入了鎖池狀態。操作系統
進入鎖池之後就會參與鎖的競爭,當它得到鎖之後還不能立刻運行,由於一個單核CPU在某一時刻,只能執行一個線程,因此他須要操做系統分配給它時間片,才能執行。因此人們一般把一個線程在得到鎖後,得到系統時間片以前的狀態稱之爲可運行狀態,但Java源代碼裏面並無可運行狀態這一說。線程
當一個持有對象鎖的線程得到CPU時間片之後,開始執行這個線程,此時叫作運行狀態。3d
當一個線程正常執行完,那麼就進入終止(死亡)狀態。系統就會回收這個線程佔用的資源。code
可是,線程的執行並非那麼順利的。一個正在運行的線程有可能會進入I/O交互,還可能調用sleep()方法,還有可能在當前線程當中有其它線程調用了join()方法。這時候線程就進入了阻塞狀態(但這也只是人們在理解的時候意淫加上去的,源代碼裏也沒有定義這一個狀態)。阻塞狀態的線程是沒有釋放對象鎖的。當I/O交互完成,或sleep()方法完成,或其它調用join()方法的線程執行完畢。阻塞狀態的線程就會恢復到可運行狀態,此時若是再次得到CPU時間片就會進入運行狀態。orm
一個處於運行狀態的線程還可能調用wait()方法、或者帶時間參數的wait(long milli)方法。這時候線程就會將對象鎖釋放,進入等待隊列裏面(若是是調用wait()方法則進入等待狀態,若是是調用帶時間參數的則進入定時等待狀態)。
一個線程若是的調用了帶時間參數的wait(long milli)方法進入了定時等待狀態,那麼只要時間一到就會進入鎖池狀態,並不須要notify()或notifyAll()方法來喚醒它。若是調用的是不帶時間參數的wait()則須要notify()或notifyAll()這兩個方法來喚醒它而後進入鎖池狀態。進入鎖池狀態之後繼續參與鎖的競爭。
當一個處於運行狀態的線程調用了suspend()方法之後,它就會進入掛起狀態(這一方法已通過時不建議使用)。掛起狀態的線程也沒有釋放對象鎖,它須要調用resume()方法之後才能恢復到可運行狀態。將線程掛起容易致使程序死鎖。
下面是我本身畫線程狀態轉換圖:
這是大多數對一個線程的狀態的理解。我之前也是這麼理解的,可是,如今我對線程的狀態有了新的理解。這與大多數人的理解有些不同。
2、我對線程狀態以及轉換的理解
下面是我對線程的本身的理解,若是有不對的地方但願懂的人能夠指出來一塊兒討論一下。
據官方源碼,一個線程有六個狀態,沒有阻塞狀態,沒有可運行,沒有掛起狀態。
因此,如今我要提出一個觀點:一個處於等待狀態、定時等待狀態的線程它也可能持有對象鎖。例如調用sleep(long millis)會使線程進入等待狀態,可是沒有釋放鎖。
線程沒有阻塞狀態,那一個正在運行的線程進入I/O,或調用sleep()方法,或當前線程當中有其它線程調用了join()方法時,線程就會進入什麼狀態呢,它總得有六個當中的一個吧。
①、當線程調用sleep()方法或當前線程中有其餘線程調用了帶時間參數的join()方法的時候進入了定時等待狀態(TIMED_WAITING)。
②、當其餘線程調用了不帶時間參數的join()方法時進入等待狀態(WAITING)。
③、當線程遇到I/O的時候仍是運行狀態(RUNNABLE)。
④、當一個線程調用了suspend()方法掛起的時候它仍是運行狀態(RUNNABLE)。
如今我要來證實一下以上四點,若是證實過程有誤,但願可以獲得指正。這些代碼的能夠直接複製來運行一下。
證實一:當線程調用sleep()方法的時候進入了定時等待狀態。
如今兩個線程t一、t2,t1持有t2的一個引用。啓動兩個線程,t2啓動後當即睡眠,讓t1打印t2的狀態。這樣就能夠看到睡眠時候的線程是六個當中的哪個狀態了。
public class Test1 { public static void main(String[] args) { Thread1 t1 = new Thread1(); Thread2 t2 = new Thread2(); t1.setThread2(t2); t1.start(); t2.start(); } } //Thread1負責打印兩個線程的狀態。 class Thread1 extends Thread { private Thread2 t2; public void setThread2(Thread2 t2) { this.t2 = t2; } @Override public void run() { System.out.println("進入t1線程"); for(int i = 0; i < 5; i++) { try { System.out.println("t1 的狀態: " + getState()); System.out.println("t2 的狀態: " + t2.getState()); System.out.println(); //爲了減小打印次數,因此t1每打印一次睡1秒 Thread.sleep(1000); } catch (InterruptedException e) { } } } } class Thread2 extends Thread { @Override public void run() { System.out.println("進入t2線程,立刻進入睡眠"); try { //睡眠5秒鐘。 sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t2睡眠結束"); } }
上面的程序執行打印的結果
說明調用sleep()方法之後線程處於定時等待狀態(TIMED_WAITING)。至於網上一直說的處於等待狀態的線程不持有對象鎖這種說法,我不知道這是官方給出的仍是人們本身定義的。
證實二:當其餘線程調用了join()方法時進入等待狀態。
如今有三個線程t1 、t2 、t3。t1負責打印三個線程的狀態。t2線程持有t3線程的引用,當進入t2線程之後,當即啓動t3線程,並調用t3.join()方法。當t3加入後t2就是等待狀態。
public class Test1 { public static void main(String[] args) { Thread1 t1 = new Thread1(); Thread2 t2 = new Thread2(); Thread3 t3 = new Thread3(); //t1須要持有t2,t3的引用,以便打印他們的狀態。 t1.setThread2(t2,t3); //t2須要持有t3的引用,以便t3可以在t2執行時加入(調用join()方法) t2.setTh3(t3); t1.start(); t2.start(); } } //Thread1負責打印全部線程的狀態。 class Thread1 extends Thread { private Thread2 t2; private Thread3 t3; public void setThread2(Thread2 t2, Thread3 t3) { this.t2 = t2; this.t3 = t3; } @Override public void run() { System.out.println("進入t1線程"); for(int i = 0; i < 5; i++) { try { System.out.println("t1 的狀態: " + getState()); System.out.println("t2 的狀態: " + t2.getState()); System.out.println("t3 的狀態: " + t3.getState()); System.out.println(); //爲了減小打印次數,因此t1每打印一次睡1秒 Thread.sleep(1000); } catch (InterruptedException e) { } } } } class Thread2 extends Thread { private Thread3 t3; public void setTh3(Thread3 t3) { this.t3 = t3; } //當進入t2線程之後立刻啓動t3線程並調用join()方法。 @Override public void run() { System.out.println("進入t2線程,t3準備加入(調用join()方法)"); t3.start(); try { t3.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t2執行結束"); } } class Thread3 extends Thread { @Override public void run() { System.out.println("進入t3線程,準備睡眠"); //原本是想讓t3線程作加法運算的,奈何電腦算太快了,因此改成睡眠。由於睡眠不釋放鎖,因此效果同樣。 try { sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t3線程結束"); } }
這是運行的打印結果,當t3加入後t2處於等待狀態
根據結果看,說明當一個正在執行的線程在其餘線程調用join()方法之後進入了等待狀態。
證實三:當一個線程調用了suspend()方法的時候它仍是運行狀態(RUNNABLE)。
package com.zcd.observe; public class Test1 { public static void main(String[] args) { Thread1 t1 = new Thread1(); Thread2 t2 = new Thread2(); //t1須要持有t2,以便打印狀態,和控制它恢復運行。 t1.setThread2(t2); t1.start(); t2.start(); } } //Thread1負責打印全部線程的狀態。 class Thread1 extends Thread { private Thread2 t2; public void setThread2(Thread2 t2) { this.t2 = t2; } @Override public void run() { System.out.println("進入t1線程"); for(int i = 0; i < 6; i++) { try { System.out.println("t1 的狀態: " + getState()); System.out.println("t2 的狀態: " + t2.getState()); System.out.println(); if(i == 3) { //恢復t2的運行。 t2.resume(); } //爲了減小打印次數,因此t1每打印一次睡1秒 Thread.sleep(1000); } catch (InterruptedException e) { } } } } class Thread2 extends Thread { @Override public void run() { System.out.println("進入t2線程,掛起"); //將線程掛起。讓t1來控制它的恢復運行。 suspend(); System.out.println("t2已經恢復運行"); System.out.println("t2正在打印1"); System.out.println("t2正在打印2"); System.out.println("t2正在打印3"); System.out.println("t2線程結束"); } }
執行結果截圖
說明:當一個線程調用了suspend()方法的時候它仍是運行狀態(RUNNABLE)。
證實四:當線程遇到I/O的時候仍是運行狀態。
package com.zcd.observe; import java.util.Scanner; public class Test1 { public static void main(String[] args) { Thread1 t1 = new Thread1(); Thread2 t2 = new Thread2(); t1.setThread2(t2); t1.start(); t2.start(); } } //Thread1負責打印全部線程的狀態。 class Thread1 extends Thread { private Thread2 t2; public void setThread2(Thread2 t2) { this.t2 = t2; } @Override public void run() { System.out.println("進入t1線程"); for(int i = 0; i < 6; i++) { try { System.out.println("t1 的狀態: " + getState()); System.out.println("t2 的狀態: " + t2.getState()); System.out.println(); //爲了減小打印次數,因此t1每打印一次睡1秒 Thread.sleep(1000); } catch (InterruptedException e) { } } System.out.println("進入t1線程結束"); } } class Thread2 extends Thread { @Override public void run() { System.out.println("進入t2線程"); //讓線程進入I/O System.out.println("請輸入數據:"); Scanner scan = new Scanner(System.in); String read = scan.nextLine(); System.out.println("您輸入的數據爲:"+read); System.out.println("t2線程結束"); } }
執行結果截圖:
說明:當線程遇到I/O的時候仍是運行狀態。
下面在附上我畫的狀態轉換圖,一共只有六個狀態。
我以爲以上兩種理解方式只是站在同的角度去理解而已。
本文爲做者原創,轉載須要聲明而且附上本文地址:http://www.cnblogs.com/GooPolaris/p/8079490.html