Java 多線程 線程的五種狀態,線程 Sleep, Wait, notify, notifyAll

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

  1. NEW 新建狀態,線程建立且沒有執行start方法時的狀態
  2. RUNNABLE 可運行狀態,線程已經啓動,可是等待相應的資源(好比IO或者時間片切換)才能開始執行
  3. BLOCKED 阻塞狀態,當遇到synchronized或者lock且沒有取得相應的鎖,就會進入這個狀態
  4. WAITING 等待狀態,當調用Object.wait或者Thread.join()且沒有設置時間,在或者LockSupport.park時,都會進入等待狀態。
  5. TIMED_WAITING 計時等待,當調用Thread.sleep()或者Object.wait(xx)或者Thread.join(xx)或者LockSupport.parkNanos或者LockSupport.partUntil,進入該狀態
  6. TERMINATED 終止狀態,線程中斷或者運行結束的狀態

 

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這幾個通常都一塊兒使用。不過須要注意下面幾個重要的點:

  1. 調用wait\notify\notifyall方法時,須要與鎖或者synchronized搭配使用,否則會報錯java.lang.IllegalMonitorStateException,由於任什麼時候刻,對象的控制權只能一個線程持有,所以調用wait等方法的時候,必須確保對其的控制權。
  2. 若是對簡單的對象調用wait等方法,若是對他們進行賦值也會報錯,由於賦值至關於修改的原有的對象,所以若是有修改需求能夠外面包裝一層。
  3. notify能夠喚醒一個在該對象上等待的線程,notifyAll能夠喚醒全部等待的線程。
  4. wait(xxx) 能夠掛起線程,並釋放對象的資源,等計時結束後自動恢復;wait()則必需要其餘線程調用notify或者notifyAll才能喚醒。
 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

相關文章
相關標籤/搜索