高并发之wait notify notifyAll原理详解
public class WaitTest { public void testWait(){ System.out.println("Start-----"); try { wait(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("End-------"); } public static void main(String[] args) { final WaitTest test = new WaitTest(); new Thread(new Runnable() { @Override public void run() { test.testWait(); } }).start(); } }
Exception in thread "Thread-0" Start----- java.lang.IllegalMonitorStateException at java.lang.Object.wait(Native Method) at org.knowledge.muti.thread.waitNotify.WaitTest.testWait(WaitTest.java:15) at org.knowledge.muti.thread.waitNotify.WaitTest$1.run(WaitTest.java:27) at java.lang.Thread.run(Thread.java:745)
这段程序并没有按我们的预期输出相应结果,而是抛出了一个异常。大家可能会觉得奇怪为什么会抛出异常?而抛出的IllegalMonitorStateException异常又是什么?我们可以看一下JDK中对IllegalMonitorStateException的描述:
Thrown to indicate that a thread has attempted to wait on an object
's monitor or to notify other threads waiting on an object'
s monitor without owning the specified monitor
这句话的意思大概就是:线程试图等待对象的监视器或者试图通知其他正在等待对象监视器的线程,但本身没有对应的监视器的所有权。wait方法是一个本地方法,其底层是通过一个叫做监视器锁的对象来完成的。所以上面之所以会抛出异常,是因为在调用wait方式时没有获取到monitor对象的所有权,那如何获取monitor对象所有权?Java中只能通过Synchronized关键字来完成,修改上述代码,增加Synchronized关键字
public class WaitTest { public synchronized void testWait(){ System.out.println("Start-----"); try { wait(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("End-------"); } public static void main(String[] args) { final WaitTest test = new WaitTest(); new Thread(new Runnable() { @Override public void run() { test.testWait(); } }).start(); } }
所以,通过这个例子,大家应该很清楚,wait方法的使用必须在同步的范围内,否则就会抛出IllegalMonitorStateException异常,wait方法的作用就是阻塞当前线程等待notify/notifyAll方法的唤醒,或等待超时后自动唤醒
notify/notifyAll方法
void notify() | Wakes up a single thread that is waiting on this object's monitor. |
void notifyAll() | Wakes up all threads that are waiting on this object's monitor. |
有了对wait方法原理的理解,notify方法和notifyAll方法就很容易理解了。既然wait方式是通过对象的monitor对象来实现的,所以只要在同一对象上去调用notify/notifyAll方法,就可以唤醒对应对象monitor上等待的线程了。notify和notifyAll的区别在于前者只能唤醒monitor上的一个线程,对其他线程没有影响,而notifyAll则唤醒所有的线程,看下面的例子很容易理解这两者的差别:
public synchronized void testWait() { System.out.println(Thread.currentThread().getName() + " Start-----"); try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " End-----"); } public static void main(String[] args) { NotifyTest obj = new NotifyTest(); for (int i = 0; i < 5; i++) { new Thread() { @Override public void run() { obj.testWait(); }; }.start(); } synchronized (obj) { obj.notify(); }
Thread.sleep(3000); System.out.println("-----------分割线-------------");
synchronized (obj) { obj.notifyAll(); } }
output:
Thread-0 Start-----
Thread-4 Start-----
Thread-3 Start-----
Thread-2 Start-----
Thread-1 Start-----
Thread-0 End-----
====================notifyAll======
Thread-1 End-----
Thread-2 End-----
Thread-3 End-----
Thread-4 End-----