首先引入下麵這段生產者和消費者的程式,店員類作為生產產品和消費產品的中介,其中的數據product為共用數據,產品最多只能囤積5個,當產品達到5個還在生產時,就會提示“產品已滿!”,類似地,如果產品只有0個了還在消費,會提示“缺貨!”: 運行程式,結果如下: 這是一種不好的情況,因為當產品已滿時,還 ...
首先引入下麵這段生產者和消費者的程式,店員類作為生產產品和消費產品的中介,其中的數據product為共用數據,產品最多只能囤積5個,當產品達到5個還在生產時,就會提示“產品已滿!”,類似地,如果產品只有0個了還在消費,會提示“缺貨!”:
1 package concurrent; 2 3 //店員類 4 class Clerk { 5 private int product = 0; 6 7 // 進貨 8 public synchronized void get() { 9 if (product >= 5) { 10 System.out.println("產品已滿!"); 11 } else { 12 System.out.println(Thread.currentThread().getName() + ":" + ++product); 13 } 14 } 15 16 // 售貨 17 public synchronized void sale() { 18 if (product <= 0) { 19 System.out.println("缺貨!"); 20 } else { 21 System.out.println(Thread.currentThread().getName() + ":" + --product); 22 } 23 } 24 } 25 26 // 生產者類 27 class Productor implements Runnable { 28 29 private Clerk clerk; 30 31 public Productor(Clerk clerk) { 32 this.clerk = clerk; 33 } 34 35 @Override 36 public void run() { 37 for (int i = 0; i < 10; i++) { 38 clerk.get(); 39 } 40 41 } 42 } 43 44 //消費者類 45 class Consumer implements Runnable { 46 47 private Clerk clerk; 48 49 public Consumer(Clerk clerk) { 50 this.clerk = clerk; 51 } 52 53 @Override 54 public void run() { 55 for (int i = 0; i < 10; i++) { 56 clerk.sale(); 57 } 58 } 59 } 60 61 public class TestProductorAndConsumer { 62 63 public static void main(String[] args) { 64 Clerk clerk = new Clerk(); 65 66 Productor productor = new Productor(clerk); 67 Consumer consumer = new Consumer(clerk); 68 69 new Thread(productor,"Productor A").start(); 70 new Thread(consumer,"Consumer B").start(); 71 } 72 }
運行程式,結果如下:
這是一種不好的情況,因為當產品已滿時,還在不停地生產,當缺貨時,還在不停地消費。為此,我們引入等待喚醒機制:
1 package concurrent; 2 3 //店員類 4 class Clerk { 5 private int product = 0; 6 7 // 進貨 8 public synchronized void get() { 9 if (product >= 5) { 10 System.out.println("產品已滿!"); 11 12 //等待 13 try { 14 this.wait(); 15 } catch (InterruptedException e) { 16 e.printStackTrace(); 17 } 18 } else { 19 System.out.println(Thread.currentThread().getName() + ":" + ++product); 20 //喚醒 21 this.notifyAll(); 22 } 23 } 24 25 // 售貨 26 public synchronized void sale() { 27 if (product <= 0) { 28 System.out.println("缺貨!"); 29 //等待 30 try { 31 this.wait(); 32 } catch (InterruptedException e) { 33 e.printStackTrace(); 34 } 35 } else { 36 System.out.println(Thread.currentThread().getName() + ":" + --product); 37 //喚醒 38 this.notifyAll(); 39 } 40 } 41 } 42 43 // 生產者類 44 class Productor implements Runnable { 45 46 private Clerk clerk; 47 48 public Productor(Clerk clerk) { 49 this.clerk = clerk; 50 } 51 52 @Override 53 public void run() { 54 for (int i = 0; i < 10; i++) { 55 clerk.get(); 56 } 57 } 58 } 59 60 //消費者類 61 class Consumer implements Runnable { 62 63 private Clerk clerk; 64 65 public Consumer(Clerk clerk) { 66 this.clerk = clerk; 67 } 68 69 @Override 70 public void run() { 71 for (int i = 0; i < 10; i++) { 72 clerk.sale(); 73 } 74 75 } 76 } 77 78 public class TestProductorAndConsumer { 79 80 public static void main(String[] args) { 81 Clerk clerk = new Clerk(); 82 83 Productor productor = new Productor(clerk); 84 Consumer consumer = new Consumer(clerk); 85 86 new Thread(productor,"Productor A").start(); 87 new Thread(consumer,"Consumer B").start(); 88 } 89 }
再運行程式,就不會再出現上述的情況:
但是,現在,我們將產品的囤積上限設定為1(這種情況在現實中也是有可能出現的):
然後運行程式:
程式的輸出貌似沒有問題,但請註意圖中箭頭所指的地方,這表示程式沒有結束,還一直在執行。這是因為,當循壞到最後一輪時,由於產品已滿引發了wait()操作,然後生產者線程等待,隨後消費者消費了一份產品,並喚醒等待的生產者線程,此時,被喚醒的生產者線程由於迴圈結束,直接結束了線程的執行,但是另一邊,消費者線程沒有結束,而且由於將產品消費完後再次進入了等待,但是生產者線程此時已經結束了,不能再喚醒消費者線程,所以便進入了死迴圈。
解決這種問題的方法時去掉Clerk類中get方法和sale方法的else,並將原來else中的代碼直接提出,這樣,就算線程結束,也會先再次喚醒等待的線程:
1 package concurrent; 2 3 //店員類 4 class Clerk { 5 private int product = 0; 6 7 // 進貨 8 public synchronized void get() { 9 if (product >= 1) { 10 System.out.println("產品已滿!"); 11 12 // 等待 13 try { 14 this.wait(); 15 } catch (InterruptedException e) { 16 e.printStackTrace(); 17 } 18 } 19 System.out.println(Thread.currentThread().getName() + ":" + ++product); 20 // 喚醒 21 this.notifyAll(); 22 } 23 24 // 售貨 25 public synchronized void sale() { 26 if (product <= 0) { 27 System.out.println("缺貨!"); 28 // 等待 29 try { 30 this.wait(); 31 } catch (InterruptedException e) { 32 e.printStackTrace(); 33 } 34 } 35 System.out.println(Thread.currentThread().getName() + ":" + --product); 36 // 喚醒 37 this.notifyAll(); 38 } 39 } 40 41 // 生產者類 42 class Productor implements Runnable { 43 44 private Clerk clerk; 45 46 public Productor(Clerk clerk) { 47 this.clerk = clerk; 48 } 49 50 @Override 51 public void run() { 52 for (int i = 0; i < 10; i++) { 53 clerk.get(); 54 } 55 } 56 } 57 58 // 消費者類 59 class Consumer implements Runnable { 60 61 private Clerk clerk; 62 63 public Consumer(Clerk clerk) { 64 this.clerk = clerk; 65 } 66 67 @Override 68 public void run() { 69 for (int i = 0; i < 10; i++) { 70 clerk.sale(); 71 } 72 } 73 } 74 75 public class TestProductorAndConsumer { 76 77 public static void main(String[] args) { 78 Clerk clerk = new Clerk(); 79 80 Productor productor = new Productor(clerk); 81 Consumer consumer = new Consumer(clerk); 82 83 new Thread(productor, "Productor A").start(); 84 new Thread(consumer, "Consumer B").start(); 85 } 86 }
運行程式,不再死迴圈:
但是,如果現在有兩個(多個)消費者線程和生產者線程,並且我們在生產者類的run方法中添加一個sleep()方法的執行,情況會如何呢?
1 package concurrent; 2 3 //店員類 4 class Clerk { 5 private int product = 0; 6 7 // 進貨 8 public synchronized void get() { 9 if (product >= 1) { 10 System.out.println("產品已滿!"); 11 12 // 等待 13 try { 14 this.wait(); 15 } catch (InterruptedException e) { 16 e.printStackTrace(); 17 } 18 } 19 System.out.println(Thread.currentThread().getName() + ":" + ++product); 20 // 喚醒 21 this.notifyAll(); 22 } 23 24 // 售貨 25 public synchronized void sale() { 26 if (product <= 0) { 27 System.out.println("缺貨!"); 28 // 等待 29 try { 30 this.wait(); 31 } catch (InterruptedException e) { 32 e.printStackTrace(); 33 } 34 } 35 System.out.println(Thread.currentThread().getName() + ":" + --product); 36 // 喚醒 37 this.notifyAll(); 38 } 39 } 40 41 // 生產者類 42 class Productor implements Runnable { 43 44 private Clerk clerk; 45 46 public Productor(Clerk clerk) { 47 this.clerk = clerk; 48 } 49 50 @Override 51 public void run() { 52 for (int i = 0; i < 10; i++) { 53 try { 54 Thread.sleep(100); 55 } catch (InterruptedException e) { 56 // TODO Auto-generated catch block 57 e.printStackTrace(); 58 } 59 clerk.get(); 60 } 61 } 62 } 63 64 // 消費者類 65 class Consumer implements Runnable { 66 67 private Clerk clerk; 68 69 public Consumer(Clerk clerk) { 70 this.clerk = clerk; 71 } 72 73 @Override 74 public void run() { 75 for (int i = 0; i < 10; i++) { 76 clerk.sale(); 77 } 78 } 79 } 80 81 public class TestProductorAndConsumer { 82 83 public static void main(String[] args) { 84 Clerk clerk = new Clerk(); 85 86 Productor productor = new Productor(clerk); 87 Consumer consumer = new Consumer(clerk); 88 89 new Thread(productor, "Productor A").start(); 90 new Thread(consumer, "Consumer B").start(); 91 new Thread(productor, "Productor C").start(); 92 new Thread(consumer, "Consumer D").start(); 93 } 94 }
運行程式:
產品數量出現了負數,這肯定是錯誤的。錯誤的原因在於,當一個消費者線程遇到產品為0時,等待,並釋放鎖標誌,然後另外一個消費者線程獲取到該鎖標誌,由於產品仍然為0,也等待,並釋放鎖標誌。這時候,生產者線程獲取到鎖,在生產一個產品後,執行notifyAll()喚醒所有線程,這時候,一個消費者線程消費一個產品使得產品為0,另外一個消費者線程再消費一個產品使得產品變為了負數,這種現象稱為虛假喚醒。在Object.wait()方法的javadoc中敘述了該如何解決這種問題:
即,將get和sale方法中的if都改為while,這樣,每次被喚醒後,都會再次判斷產品數是否>=0:
1 package concurrent; 2 3 //店員類 4 class Clerk { 5 private int product = 0; 6 7 // 進貨 8 public synchronized void get() { 9 while (product >= 1) { 10 System.out.println("產品已滿!"); 11 12 // 等待 13 try { 14 this.wait(); 15 } catch (InterruptedException e) { 16 e.printStackTrace(); 17 } 18 } 19 System.out.println(Thread.currentThread().getName() + ":" + ++product); 20 // 喚醒 21 this.notifyAll(); 22 } 23 24 // 售貨 25 public synchronized void sale() { 26 while (product <= 0) { 27 System.out.println("缺貨!"); 28 // 等待 29 try { 30 this.wait(); 31 } catch (InterruptedException e) { 32 e.printStackTrace(); 33 } 34 } 35 System.out.println(Thread.currentThread().getName() + ":" + --product); 36 // 喚醒 37 this.notifyAll(); 38 } 39 } 40 41 // 生產者類 42 class Productor implements Runnable { 43 44 private Clerk clerk; 45 46 public Productor(Clerk clerk) { 47 this.clerk = clerk; 48 } 49 50 @Override 51 public void run() { 52 for (int i = 0; i < 10; i++) { 53 try { 54 Thread.sleep(100); 55 } catch (InterruptedException e) { 56 // TODO Auto-generated catch block 57 e.printStackTrace(); 58 } 59 clerk.get(); 60 } 61 } 62 } 63 64 // 消費者類 65 class Consumer implements Runnable { 66 67 private Clerk clerk; 68 69 public Consumer(Clerk clerk) { 70 this.clerk = clerk; 71 } 72 73 @Override 74 public void run() { 75 for (int i = 0; i < 10; i++) { 76 clerk.sale(); 77 } 78 } 79 } 80 81 public class TestProductorAndConsumer { 82 83 public static void main(String[] args) { 84 Clerk clerk = new Clerk(); 85 86 Productor productor = new Productor(clerk); 87 Consumer consumer = new Consumer(clerk); 88 89 new Thread(productor, "Productor A").start(); 90 new Thread(consumer, "Consumer B").start(); 91 new Thread(productor, "Productor C").start(); 92 new Thread(consumer, "Consumer D").start(); 93 } 94 }
運行程式,發現結果終於正常了:
轉載自:http://blog.csdn.net/xiangwanpeng/article/details/54973782