最近看帖子,发现一道面试题: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 状态的线程。
因此,咱们须要根据实际的业务场景来考虑如何使用。