1、先來看看Thread類裏面都有哪幾種狀態,在Thread.class中能夠找到這個枚舉,它定義了線程的相關狀態:html
1 public enum State { 2 NEW, 3 RUNNABLE, 4 BLOCKED, 5 WAITING, 6 TIMED_WAITING, 7 TERMINATED; 8 }
具體解釋請見源碼,下面簡單解釋下Thread的五種狀態何時出現:java
Object.wait
或者Thread.join()
且沒有設置時間,在或者LockSupport.park
時,都會進入等待狀態。Thread.sleep()
或者Object.wait(xx)
或者Thread.join(xx)
或者LockSupport.parkNanos
或者LockSupport.partUntil
時,進入該狀態
2、Sleep 與 Wait 的區別spring
因爲wait方法是在Object上的,而sleep方法是在Thread上,當調用Thread.sleep時,並不能改變對象的狀態,所以也不會釋放鎖。請看下面代碼結果:ide
1 package springBootExample.example.simpleApplication; 2 3 public class TestThread { 4 5 public static void main(String[] args) { 6 Room room = new Room(); 7 Thread man = new Thread(room, "男人"); 8 Thread female = new Thread(room, "女人"); 9 System.out.println("After new but before start thread name = "+man.getName()+" state = "+man.getState()); 10 // 此時的man和female處於NEW狀態 11 man.start(); 12 System.out.println("After start Thread name ="+man.getName()+" state = "+man.getState()); 13 female.start(); 14 // 此時的man和female處於Runnable狀態,可是等待相應的資源(好比IO或者時間片切換)才能開始執行,誰先得到資源就能夠執行 15 System.out.println("小姐已經接待完客人"); 16 } 17 18 } 19 20 class Room implements Runnable { 21 public int count = 1; 22 23 @Override 24 public void run() { 25 26 while (count <= 20) { 27 // BLOCKED 阻塞狀態,當遇到synchronized或者lock且沒有取得相應的鎖,就會進入這個狀態 28 synchronized (this) { 29 System.out.println(Thread.currentThread().getName() + "去小姐的房間,小姐累計接待客人:" + count + "個."); 30 count++; 31 try { 32 Thread.currentThread().sleep(100); 33 // this.wait(100); 34 } catch (InterruptedException e) { 35 // TODO Auto-generated catch block 36 e.printStackTrace(); 37 } 38 } 39 } 40 41 } 42 43 }
結果:this
1 After new but before start thread name = 男人 state = NEW 2 After start Thread name =男人 state = RUNNABLE 3 男人去小姐的房間,小姐累計接待客人:1個. 4 小姐已經接待完客人 5 男人去小姐的房間,小姐累計接待客人:2個. 6 男人去小姐的房間,小姐累計接待客人:3個. 7 男人去小姐的房間,小姐累計接待客人:4個. 8 男人去小姐的房間,小姐累計接待客人:5個. 9 男人去小姐的房間,小姐累計接待客人:6個. 10 男人去小姐的房間,小姐累計接待客人:7個. 11 男人去小姐的房間,小姐累計接待客人:8個. 12 男人去小姐的房間,小姐累計接待客人:9個. 13 男人去小姐的房間,小姐累計接待客人:10個. 14 男人去小姐的房間,小姐累計接待客人:11個. 15 男人去小姐的房間,小姐累計接待客人:12個. 16 男人去小姐的房間,小姐累計接待客人:13個. 17 男人去小姐的房間,小姐累計接待客人:14個. 18 男人去小姐的房間,小姐累計接待客人:15個. 19 男人去小姐的房間,小姐累計接待客人:16個. 20 男人去小姐的房間,小姐累計接待客人:17個. 21 男人去小姐的房間,小姐累計接待客人:18個. 22 男人去小姐的房間,小姐累計接待客人:19個. 23 男人去小姐的房間,小姐累計接待客人:20個. 24 女人去小姐的房間,小姐累計接待客人:21個.
從上面的結果能夠看出,NEW狀態在新建立一個線程時呈現,RUNNABLE是在線程調用start()方法。由於線程得到資源就能夠執行,在main()方法中新建一個線程man.start()執行,所以新線程得到資源就能夠執行,從第4行結果看出。spa
注意看最後面有一個女人。這是由於synchronized的代碼同步時在while循環裏面,所以最後一次男人和女人都進入到了while裏面,而後纔開始等待相應的鎖。這就致使第20次執行完輪到了女人。線程
當調用wait時:code
1 @Override 2 public void run() { 3 4 while (count <= 20) { 5 // BLOCKED 阻塞狀態,當遇到synchronized或者lock且沒有取得相應的鎖,就會進入這個狀態 6 // System.out.println("Before synchronized thread name = "+Thread.currentThread().getName()+" state = "+Thread.currentThread().getState()); 7 synchronized (this) { 8 System.out.println(Thread.currentThread().getName() + "去小姐的房間,小姐累計接待客人:" + count + "個."); 9 count++; 10 try { 11 // Thread.currentThread().sleep(100); 12 this.wait(100); 13 } catch (InterruptedException e) { 14 // TODO Auto-generated catch block 15 e.printStackTrace(); 16 } 17 } 18 }
結果:htm
1 After new but before start thread name = 男人 state = NEW 2 After start Thread name =男人 state = RUNNABLE 3 小姐已經接待完客人 4 男人去小姐的房間,小姐累計接待客人:1個. 5 女人去小姐的房間,小姐累計接待客人:2個. 6 男人去小姐的房間,小姐累計接待客人:3個. 7 女人去小姐的房間,小姐累計接待客人:4個. 8 女人去小姐的房間,小姐累計接待客人:5個. 9 男人去小姐的房間,小姐累計接待客人:6個. 10 女人去小姐的房間,小姐累計接待客人:7個. 11 男人去小姐的房間,小姐累計接待客人:8個. 12 男人去小姐的房間,小姐累計接待客人:9個. 13 女人去小姐的房間,小姐累計接待客人:10個. 14 男人去小姐的房間,小姐累計接待客人:11個. 15 女人去小姐的房間,小姐累計接待客人:12個. 16 男人去小姐的房間,小姐累計接待客人:13個. 17 女人去小姐的房間,小姐累計接待客人:14個. 18 男人去小姐的房間,小姐累計接待客人:15個. 19 女人去小姐的房間,小姐累計接待客人:16個. 20 男人去小姐的房間,小姐累計接待客人:17個. 21 女人去小姐的房間,小姐累計接待客人:18個. 22 男人去小姐的房間,小姐累計接待客人:19個. 23 女人去小姐的房間,小姐累計接待客人:20個.
可是若是稍做修改就會出現弄一種狀況,代碼以下:對象
1 class Room implements Runnable { 2 public int count = 1; 3 4 @Override 5 public void run() { 6 7 while (count <= 20) { 8 // BLOCKED 阻塞狀態,當遇到synchronized或者lock且沒有取得相應的鎖,就會進入這個狀態 9 System.out.println("Before synchronized thread name = "+Thread.currentThread().getName()+" state = "+Thread.currentThread().getState()); 10 synchronized (this) { 11 System.out.println(Thread.currentThread().getName() + "去小姐的房間,小姐累計接待客人:" + count + "個."); 12 count++; 13 try { 14 Thread.currentThread().sleep(100); 15 // this.wait(100); 16 } catch (InterruptedException e) { 17 // TODO Auto-generated catch block 18 e.printStackTrace(); 19 } 20 } 21 } 22 23 }
結果:
1 After new but before start thread name = 男人 state = NEW 2 After start Thread name =男人 state = RUNNABLE 3 Before synchronized thread name = 男人 state = RUNNABLE 4 小姐已經接待完客人 5 男人去小姐的房間,小姐累計接待客人:1個. 6 Before synchronized thread name = 女人 state = RUNNABLE 7 Before synchronized thread name = 男人 state = RUNNABLE 8 女人去小姐的房間,小姐累計接待客人:2個. 9 Before synchronized thread name = 女人 state = RUNNABLE 10 男人去小姐的房間,小姐累計接待客人:3個. 11 Before synchronized thread name = 男人 state = RUNNABLE 12 女人去小姐的房間,小姐累計接待客人:4個. 13 Before synchronized thread name = 女人 state = RUNNABLE 14 男人去小姐的房間,小姐累計接待客人:5個. 15 Before synchronized thread name = 男人 state = RUNNABLE 16 女人去小姐的房間,小姐累計接待客人:6個. 17 Before synchronized thread name = 女人 state = RUNNABLE 18 男人去小姐的房間,小姐累計接待客人:7個. 19 Before synchronized thread name = 男人 state = RUNNABLE 20 女人去小姐的房間,小姐累計接待客人:8個. 21 Before synchronized thread name = 女人 state = RUNNABLE 22 男人去小姐的房間,小姐累計接待客人:9個. 23 Before synchronized thread name = 男人 state = RUNNABLE 24 女人去小姐的房間,小姐累計接待客人:10個. 25 Before synchronized thread name = 女人 state = RUNNABLE 26 男人去小姐的房間,小姐累計接待客人:11個. 27 Before synchronized thread name = 男人 state = RUNNABLE 28 女人去小姐的房間,小姐累計接待客人:12個. 29 Before synchronized thread name = 女人 state = RUNNABLE 30 男人去小姐的房間,小姐累計接待客人:13個. 31 Before synchronized thread name = 男人 state = RUNNABLE 32 女人去小姐的房間,小姐累計接待客人:14個. 33 Before synchronized thread name = 女人 state = RUNNABLE 34 男人去小姐的房間,小姐累計接待客人:15個. 35 Before synchronized thread name = 男人 state = RUNNABLE 36 女人去小姐的房間,小姐累計接待客人:16個. 37 Before synchronized thread name = 女人 state = RUNNABLE 38 男人去小姐的房間,小姐累計接待客人:17個. 39 Before synchronized thread name = 男人 state = RUNNABLE 40 女人去小姐的房間,小姐累計接待客人:18個. 41 Before synchronized thread name = 女人 state = RUNNABLE 42 男人去小姐的房間,小姐累計接待客人:19個. 43 Before synchronized thread name = 男人 state = RUNNABLE 44 女人去小姐的房間,小姐累計接待客人:20個. 45 男人去小姐的房間,小姐累計接待客人:21個.
目前這種現象暫時還不是特別清楚原理,可是當男人和女人都在while循環等待時,Thread.currentThread().getName() 會獲取當前線程的名字,而在循環中再獲取當前名字時會出現這種交替的狀況?其實Room資源一直是男人擁有。
3、Wait(), Notify() , NotifyAll()的使用
wait、notify、notifyall這幾個通常都一塊兒使用。不過須要注意下面幾個重要的點:
java.lang.IllegalMonitorStateException
,由於任什麼時候刻,對象的控制權只能一個線程持有,所以調用wait等方法的時候,必須確保對其的控制權。1 package springBootExample.example.simpleApplication; 2 3 public class TestWaitAndNotify { 4 Call call = new Call(false); 5 6 class MaMa extends Thread { 7 public MaMa(String name) { 8 super(name); 9 } 10 11 @Override 12 public void run() { 13 synchronized (call) { 14 try { 15 call.wait(3000); 16 } catch (InterruptedException e) { 17 // TODO Auto-generated catch block 18 e.printStackTrace(); 19 } 20 call.setFlag(true); 21 // call.notifyAll(); 22 for (int i = 0; i < 3; i++) { 23 System.out.println("進來一個吧"); 24 call.notify(); 25 try { 26 call.wait(1000); 27 } catch (InterruptedException e) { 28 e.printStackTrace(); 29 } 30 } 31 } 32 33 } 34 35 } 36 37 class Customer extends Thread { 38 public Customer(String name) { 39 super(name); 40 } 41 42 @Override 43 public void run() { 44 synchronized (call) { 45 while (!call.isFlag()) { 46 System.out.println(Thread.currentThread().getName() + "等待王媽媽的呼喚"); 47 try { 48 call.wait(); 49 } catch (InterruptedException e) { 50 // TODO Auto-generated catch block 51 e.printStackTrace(); 52 } 53 } 54 System.out.println(Thread.currentThread().getName() + "進入小姐的房間"); 55 } 56 } 57 } 58 59 public static void main(String[] args) { 60 TestWaitAndNotify test = new TestWaitAndNotify(); 61 MaMa teacher = test.new MaMa("王媽媽"); 62 Customer stu1 = test.new Customer("小米"); 63 Customer stu2 = test.new Customer("小百"); 64 Customer stu3 = test.new Customer("小阿"); 65 teacher.start(); 66 stu1.start(); 67 stu2.start(); 68 stu3.start(); 69 70 } 71 72 } 73 74 class Call { 75 private boolean flag = false; 76 77 public Call(boolean flag) { 78 this.flag = flag; 79 } 80 81 public boolean isFlag() { 82 return flag; 83 } 84 85 public void setFlag(boolean flag) { 86 this.flag = flag; 87 } 88 89 }
上面代碼中21,24行包含了notify() 和notifyAll()方法的,61行注意內部類實例時的方法。代碼的運行結果也會不相同,notify()輸出的結果爲:
小米等待王媽媽的呼喚
小阿等待王媽媽的呼喚
小百等待王媽媽的呼喚
進來一個吧
小米進入小姐的房間
進來一個吧
小阿進入小姐的房間
進來一個吧
小百進入小姐的房間
notifyAll()輸出的結果爲:
小米等待王媽媽的呼喚
小阿等待王媽媽的呼喚
小百等待王媽媽的呼喚
小百進入小姐的房間 小阿進入小姐的房間 小米進入小姐的房間
Reference
[1] http://www.cnblogs.com/xing901022/p/7846809.html