1. wait方法和notify方法
這兩個方法,包括notifyAll方法,都是Object類中的方法。在Java API中,wait方法的定義如下:
public final void wait() throws InterruptedExceptionCauses the current thread to wait until another thread invokes thenotify()
method or thenotifyAll()
method for this object. In other words, this method behaves exactly as if it simply performs the callwait(0)
.The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the
notify
method or thenotifyAll
method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:
synchronized (obj) { while (<condition does not hold>) obj.wait(); ... // Perform action appropriate to condition }This method should only be called by a thread that is the owner of this object's monitor. See thenotify
method for a description of the ways in which a thread can become the owner of a monitor.
在 Java 中可以用 wait、notify 和 notifyAll 來實現線程間的通信。線程在運行的時候,如果發現某些條件沒有被滿足,可以調用wait方法暫停自己的執行,並且放棄已經獲得的鎖,然后進入等待狀態。當該線程被其他線程喚醒並獲得鎖后,可以沿着之前暫停的地方繼續向后執行,而不是再次從同步代碼塊開始的地方開始執行。但是需要注意的一點是,對線程等待的條件的判斷要使用while而不是if來進行判斷。這樣在線程被喚醒后,會再次判斷條件是否正真滿足。
想象一下一個生產者,多個消費者的場景。一個消費者Consumer1了最后一個元素,並且喚醒了其他線程,如果被喚醒的正好是Consumer2,那么此時是沒有元素可以消費的。如果用的是if判斷,那么被喚醒后就不會再次進行條件的判斷,而是直接向下執行導致運行錯誤。我們的代碼如下:
notify方法會喚醒等待一個對象鎖的線程,但是具體喚醒哪個是不確定的。
2. 實現生產者消費者
如下使用if來判斷會導致程序出錯
一定要使用while來進行判斷線程的等待條件而不是使用if
1 package thread.learn; 2 3 import java.util.LinkedList; 4 import java.util.Queue; 5 import java.util.Random; 6 7 /** 8 * Created by liujinhong on 2017/4/2. 9 * 生產者消費者問題是一個很經典的問題,值得好好研究一下 10 * java的wait和notify方法在使用時也是要非常注意的 11 */ 12 public class ProducerConsumer { 13 public static class Producer extends Thread { 14 Queue<Integer> queue; 15 int maxsize; 16 17 Producer(Queue<Integer> queue, int maxsize, String name) { 18 this.queue = queue; 19 this.maxsize = maxsize; 20 this.setName(name); 21 } 22 @Override 23 public void run() { 24 while (true) { 25 synchronized (queue) { 26 try{ 27 Thread.sleep(500); 28 } catch (Exception e) {} 29 30 System.out.println(this.getName() + "獲得隊列的鎖"); 31 //條件的判斷一定要使用while而不是if 32 if (queue.size() == maxsize) { 33 System.out.println("隊列已滿,生產者" + this.getName() + "等待"); 34 try { 35 queue.wait(); 36 } catch (Exception e) {} 37 } 38 int num = (int)(Math.random()*100); 39 queue.offer(num); 40 41 System.out.println(this.getName() + "生產一個元素:" + num); 42 queue.notifyAll(); 43 44 System.out.println(this.getName() + "退出一次生產過程!"); 45 } 46 } 47 } 48 } 49 50 public static class Consumer extends Thread { 51 Queue<Integer> queue; 52 int maxsize; 53 54 Consumer(Queue<Integer> queue, int maxsize, String name) { 55 this.queue = queue; 56 this.maxsize = maxsize; 57 this.setName(name); 58 } 59 60 @Override 61 public void run() { 62 while (true) { 63 synchronized (queue) { 64 try{ 65 Thread.sleep(500); 66 } catch (Exception e) {} 67 68 System.out.println(this.getName() + "獲得隊列的鎖"); 69 //條件的判斷一定要使用while而不是if 70 if (queue.isEmpty()) { 71 System.out.println("隊列為空,消費者" + this.getName() + "等待"); 72 try{ 73 queue.wait(); 74 } catch (Exception e) {} 75 } 76 int num = queue.poll(); 77 System.out.println(this.getName() + "消費一個元素:"+num); 78 queue.notifyAll(); 79 80 System.out.println(this.getName() + "退出一次消費過程!"); 81 } 82 } 83 } 84 } 85 86 public static void main(String[] args) { 87 Queue<Integer> queue = new LinkedList<>(); 88 int maxsize = 2; 89 90 Producer producer = new Producer(queue, maxsize, "Producer"); 91 Consumer consumer1 = new Consumer(queue, maxsize,"Consumer1"); 92 Consumer consumer2 = new Consumer(queue, maxsize,"Consumer2"); 93 Consumer consumer3 = new Consumer(queue, maxsize,"Consumer3"); 94 95 producer.start(); 96 consumer1.start(); 97 consumer2.start(); 98 consumer3.start(); 99 } 100 }