1. 程式人生 > >多執行緒之Java中的等待喚醒機制

多執行緒之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 }

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