最近看帖子,發現一道面試題:java
啓動兩個線程, 一個輸出 1,3,5,7…99, 另外一個輸出 2,4,6,8…100 最後 STDOUT 中按序輸出 1,2,3,4,5…100
題目要求用 Java 的 wait + notify 機制來實現,重點考察對於多線程可見性的理解。面試
wait 和 notify 均爲 Object 的方法:多線程
從以上的定義中,咱們能夠了解到如下事實:ide
wait()
和notify()
來實現不一樣的線程間的可見。在使用 wait 和 notify 以前,咱們須要先了解對象的控制權(monitor)。在 Java 中任何一個時刻,對象的控制權只能被一個線程擁有。如何理解控制權呢?請先看下面的簡單代碼:this
public class ThreadTest { public static void main(String[] args) { Object object = new Object(); new Thread(new Runnable() { @Override public void run() { try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } }
直接執行,咱們將會獲得如下異常:線程
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:502) at com.xiangyu.demo.ThreadTest$1.run(ThreadTest.java:10) at java.lang.Thread.run(Thread.java:748)
出錯的代碼在:object.wait();
。這裏咱們須要瞭解如下事實:code
在上面的示例代碼中,咱們 new 了一個 Thread,可是對象 object 的控制權仍在主線程裏。因此會報 java.lang.IllegalMonitorStateException 。對象
咱們能夠經過同步鎖來得到對象控制權,例如:synchronized 代碼塊。對以上的示例代碼作改造:同步
public class ThreadTest { public static void main(String[] args) { Object object = new Object(); new Thread(new Runnable() { @Override public void run() { synchronized (object){ // 修改處 try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }
再次執行,代碼再也不報錯。it
咱們能夠獲得如下結論:
wait()
和notify()
方法,須要先取得對象的控制權synchronized (object)
來取得對於 object 對象的控制權瞭解了對象控制權以後,咱們就能夠正常地使用 notify 和 wait 了,下面給出個人解題方法,供參考。
public class ThreadTest { private final Object flag = new Object(); public static void main(String[] args) { ThreadTest threadTest = new ThreadTest(); ThreadA threadA = threadTest.new ThreadA(); threadA.start(); ThreadB threadB = threadTest.new ThreadB(); threadB.start(); } class ThreadA extends Thread { @Override public void run() { synchronized (flag) { for (int i = 0; i <= 100; i += 2) { flag.notify(); System.out.println(i); try { flag.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } class ThreadB extends Thread { @Override public void run() { synchronized (flag) { for (int i = 1; i < 100; i += 2) { flag.notify(); System.out.println(i); try { flag.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
notify()
和notifyAll()
這兩個方法均爲 native 方法,在JDK 1.8 中的關於notify()
的JavaDoc以下:
Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened.
譯爲:
喚醒此 object 控制權下的一個處於 wait 狀態的線程。如有多個線程處於此 object 控制權下的 wait 狀態,只有一個會被喚醒。
也就是說,若是有多個線程在 wait 狀態,咱們並不知道哪一個線程會被喚醒。
在JDK 1.8 中的關於notifyAll()
的JavaDoc以下:
Wakes up all threads that are waiting on this object's monitor.
譯爲:
喚醒全部處於此 object 控制權下的 wait 狀態的線程。
因此,咱們須要根據實際的業務場景來考慮如何使用。