多線程之Java中的等待喚醒機制


  多線程的問題中的經典問題是生產者和消費者的問題,就是如何讓線程有序的進行執行,獲取CPU執行時間片的過程是隨機的,如何能夠讓線程有序的進行,Java中提供了等待喚醒機制很好的解決了這個問題!

  生產者消費者經典的線程中的問題其實是解決線程中的通訊問題,就是不同種類的線程針對同一資源的操作,這里其實有一張圖很好的闡述了這其中的問題:

  1 //代碼中的實體類
  2 public class Student {
  3     String name;
  4     int age;
  5     boolean flag; // 默認情況是沒有數據,如果是true,說明有數據
  6 }
  7 
  8 public class SetThread implements Runnable {
  9 
 10     private Student s;
 11     private int x = 0;
 12 
 13     public SetThread(Student s) {
 14         this.s = s;
 15     }
 16 
 17     @Override
 18     public void run() {
 19         while (true) {
 20             synchronized (s) {
 21                 //判斷有沒有
 22                 if(s.flag){
 23                     try {
 24                         s.wait(); //t1等着,釋放鎖
 25                     } catch (InterruptedException e) {
 26                         e.printStackTrace();
 27                     }
 28                 }
 29                 
 30                 if (x % 2 == 0) {
 31                     s.name = "林青霞";
 32                     s.age = 27;
 33                 } else {
 34                     s.name = "劉意";
 35                     s.age = 30;
 36                 }
 37                 x++; //x=1
 38                 
 39                 //修改標記
 40                 s.flag = true;
 41                 //喚醒線程
 42                 s.notify(); //喚醒t2,喚醒並不表示你立馬可以執行,必須還得搶CPU的執行權。
 43             }
 44             //t1有,或者t2有
 45         }
 46     }
 47 }
 48 
 49 public class GetThread implements Runnable {
 50     private Student s;
 51 
 52     public GetThread(Student s) {
 53         this.s = s;
 54     }
 55 
 56     @Override
 57     public void run() {
 58         while (true) {
 59             synchronized (s) {
 60                 if(!s.flag){
 61                     try {
 62                         s.wait(); //t2就等待了。立即釋放鎖。將來醒過來的時候,是從這里醒過來的時候
 63                     } catch (InterruptedException e) {
 64                         e.printStackTrace();
 65                     }
 66                 }
 67                 
 68                 System.out.println(s.name + "---" + s.age);
 69                 //林青霞---27
 70                 //劉意---30
 71                 
 72                 //修改標記
 73                 s.flag = false;
 74                 //喚醒線程
 75                 s.notify(); //喚醒t1
 76             }
 77         }
 78     }
 79 }
 80 
 81 /*
 82  * 分析:
 83  *         資源類:Student    
 84  *         設置學生數據:SetThread(生產者)
 85  *         獲取學生數據:GetThread(消費者)
 86  *         測試類:StudentDemo
 87  * 
 88  * 問題1:按照思路寫代碼,發現數據每次都是:null---0
 89  * 原因:我們在每個線程中都創建了新的資源,而我們要求的時候設置和獲取線程的資源應該是同一個
 90  * 如何實現呢?
 91  *         在外界把這個數據創建出來,通過構造方法傳遞給其他的類。
 92  * 
 93  * 問題2:為了數據的效果好一些,我加入了循環和判斷,給出不同的值,這個時候產生了新的問題
 94  *         A:同一個數據出現多次
 95  *         B:姓名和年齡不匹配
 96  * 原因:
 97  *         A:同一個數據出現多次
 98  *             CPU的一點點時間片的執行權,就足夠你執行很多次。
 99  *         B:姓名和年齡不匹配
100  *             線程運行的隨機性
101  * 線程安全問題:
102  *         A:是否是多線程環境        是
103  *         B:是否有共享數據        是
104  *         C:是否有多條語句操作共享數據    是
105  * 解決方案:
106  *         加鎖。
107  *         注意:
108  *             A:不同種類的線程都要加鎖。
109  *             B:不同種類的線程加的鎖必須是同一把。
110  * 
111  * 問題3:雖然數據安全了,但是呢,一次一大片不好看,我就想依次的一次一個輸出。
112  * 如何實現呢?
113  *         通過Java提供的等待喚醒機制解決。
114  * 
115  * 等待喚醒:
116  *         Object類中提供了三個方法:
117  *             wait():等待
118  *             notify():喚醒單個線程
119  *             notifyAll():喚醒所有線程
120  *         為什么這些方法不定義在Thread類中呢?
121  *             這些方法的調用必須通過鎖對象調用,而我們剛才使用的鎖對象是任意鎖對象。
122  *             所以,這些方法必須定義在Object類中。
123  */
124 public class StudentDemo {
125     public static void main(String[] args) {
126         //創建資源
127         Student s = new Student();
128         
129         //設置和獲取的類
130         SetThread st = new SetThread(s);
131         GetThread gt = new GetThread(s);
132 
133         //線程類
134         Thread t1 = new Thread(st);
135         Thread t2 = new Thread(gt);
136 
137         //啟動線程
138         t1.start();
139         t2.start();
140     }
141 }

線程的狀態轉換圖及常見執行情況:

 

上述代碼的優化方案:

 1 //創建對象的時候實現線程安全
 2 public class Student {
 3     private String name;
 4     private int age;
 5     private boolean flag; // 默認情況是沒有數據,如果是true,說明有數據
 6 
 7     public synchronized void set(String name, int age) {
 8         // 如果有數據,就等待
 9         if (this.flag) {
10             try {
11                 this.wait();
12             } catch (InterruptedException e) {
13                 e.printStackTrace();
14             }
15         }
16 
17         // 設置數據
18         this.name = name;
19         this.age = age;
20 
21         // 修改標記
22         this.flag = true;
23         this.notify();
24     }
25 
26     public synchronized void get() {
27         // 如果沒有數據,就等待
28         if (!this.flag) {
29             try {
30                 this.wait();
31             } catch (InterruptedException e) {
32                 e.printStackTrace();
33             }
34         }
35 
36         // 獲取數據
37         System.out.println(this.name + "---" + this.age);
38 
39         // 修改標記
40         this.flag = false;
41         this.notify();
42     }
43 }
44 
45 public class GetThread implements Runnable {
46     private Student s;
47 
48     public GetThread(Student s) {
49         this.s = s;
50     }
51 
52     @Override
53     public void run() {
54         while (true) {
55             s.get();
56         }
57     }
58 }
59 
60 public class SetThread implements Runnable {
61 
62     private Student s;
63     private int x = 0;
64 
65     public SetThread(Student s) {
66         this.s = s;
67     }
68 
69     @Override
70     public void run() {
71         while (true) {
72             if (x % 2 == 0) {
73                 s.set("林青霞", 27);
74             } else {
75                 s.set("劉意", 30);
76             }
77             x++;
78         }
79     }
80 }
81 
82 public class StudentDemo {
83     public static void main(String[] args) {
84         //創建資源
85         Student s = new Student();
86         
87         //設置和獲取的類
88         SetThread st = new SetThread(s);
89         GetThread gt = new GetThread(s);
90 
91         //線程類
92         Thread t1 = new Thread(st);
93         Thread t2 = new Thread(gt);
94 
95         //啟動線程
96         t1.start();
97         t2.start();
98     }
99 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM