等待通知机制
等待通知机制的实现
方法wait()的作用是使当前执行代码的线程进行等待,wait方法是object类的方法,作用就是使当前的线程进入预执行队列中,并且在wait方法所在的代码块处停止执行,直到接到通知或者中断位置。在调用wait之前,线程必须获得对象级别的锁,即只能在同步方法或者同步代码块中调用wait方法。
当前线程执行完wait方法后,当前线程会释放掉对象锁,在wait方法返回之前,线程和其他线程竞争重新获得锁。
唤醒线程采用notify方法,该方法用来通知哪些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则有线程规划器随机挑出一个呈wait状态的线程,对其发出通知notify。 需要知道的是当线程发出notify通知以后当前线程不会马上释放锁该对象锁,呈现wait状态的线程也不能马上获得该对象锁,需要等待notify方法的线程执行完以后才能获得该对象锁。
wait的一个线程获得锁以后,会释放掉当前线程的锁,此时如果没有对象再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有接到该对象的通知,还会继续阻塞在wait状态,直到这个对象发出notify或者notifyall通知。
请看如下程序:
public class MainRun { public static void main(String[] args) { String string = new String(""); try { string.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }}
输出如下
Exception in thread "main" java.lang.IllegalMonitorStateException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:503) at chaprer3.MainRun.main(MainRun.java:19) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
出现异常的原因就是没有给对象加锁,也就是没有同步加锁器,必须是对象级别的锁(不明白参考第二章)。
wait和notify使用实例:
public class PublicObject {}public class ThreadA extends Thread { private PublicObject object; public ThreadA(PublicObject object) { this.object = object; } @Override synchronized public void run() { synchronized (this.object) { System.out.println("TheadTest1将要进行wait方法了"); try { this.object.wait(); System.out.println("之前就已经释放锁了,我现在只有重新得到锁才可以运行"); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("TheadTest1将要结束了"); } }}public class ThreadB extends Thread { private PublicObject object; public ThreadB(PublicObject object) { this.object = object; } @Override public void run() { synchronized (this.object) { System.out.println("我将要发出通知了"); this.object.notify(); System.out.println("我就是不马上释放锁,我要把这个代码块运行完才释放"); System.out.println("通知结束了"); } }} public class MainRun { public static void main(String[] args) throws InterruptedException { PublicObject object = new PublicObject(); ThreadA threadA = new ThreadA(object); threadA.start(); Thread.sleep(2000); ThreadB threadB = new ThreadB(object); threadB.start(); }}
输出结果
TheadTest1将要进行wait方法了我将要发出通知了我就是不马上释放锁,我要把这个代码块运行完才释放通知结束了之前就已经释放锁了,我现在只有重新得到锁才可以运行TheadTest1将要结束了
interupt方法遇到wait方法。
当线程呈现wait状态的时候,调用线程对象的interrupt方法会出现interruptedException异常。
public class ThreadA extends Thread { private PublicObject object; public ThreadA(PublicObject object) { this.object = object; } @Override synchronized public void run() { synchronized (this.object) { System.out.println("TheadTest1将要进行wait方法了"); try { this.object.wait(); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("出现异常了,因为wait方法被interrupt了!"); } } }}public class MainRun { public static void main(String[] args) throws InterruptedException { PublicObject object = new PublicObject(); ThreadA threadA = new ThreadA(object); threadA.start(); Thread.sleep(2000); threadA.interrupt(); }}
输出结果
TheadTest1将要进行wait方法了java.lang.InterruptedException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:503) at chaprer3.ThreadA.run(ThreadA.java:21)出现异常了,因为wait方法被interrupt了!
notify方法只通知一个线程
notify方法只随机通知一个线程进行唤醒。
public class PublicObject {}public class ThreadA extends Thread { private PublicObject object; public ThreadA(PublicObject object) { this.object = object; } @Override synchronized public void run() { synchronized (this.object) { try { System.out.println("开始等待"+Thread.currentThread().getName()); this.object.wait(); System.out.println("已经唤醒"+Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }}public class ThreadB extends Thread { private PublicObject object; public ThreadB(PublicObject object) { this.object = object; } @Override public void run() { synchronized (this.object) { System.out.println("唤醒触发一次"); this.object.notify(); } }} public class MainRun { public static void main(String[] args) throws InterruptedException { PublicObject object = new PublicObject(); ThreadA threadA1 = new ThreadA(object); threadA1.start(); ThreadA threadA2 = new ThreadA(object); threadA2.start(); ThreadA threadA3 = new ThreadA(object); threadA3.start(); Thread.sleep(1000); ThreadB threadB = new ThreadB(object); threadB.start(); ThreadB threadB1 = new ThreadB(object); threadB1.start(); ThreadB threadB3 = new ThreadB(object); threadB3.start(); ThreadB threadB4 = new ThreadB(object); threadB4.start(); ThreadB threadB5 = new ThreadB(object); threadB5.start(); }}
输出结果
开始等待Thread-0开始等待Thread-1开始等待Thread-2唤醒触发一次已经唤醒Thread-0唤醒触发一次已经唤醒Thread-1唤醒触发一次已经唤醒Thread-2唤醒触发一次唤醒触发一次
程序多次使用notify来唤醒线程,但是如果有非常多的线程的时候就很难保证所有的线程都被唤醒了。
唤醒所有线程
当notify调用的次数小于线程对象的数量的时候,会出现部分线程无法被唤醒的情况,为了唤醒全部的线程,可以使用notifyAll方法,稍微修改线程B的代码
public class ThreadB extends Thread { private PublicObject object; public ThreadB(PublicObject object) { this.object = object; } @Override public void run() { synchronized (this.object) { System.out.println("唤醒所有线程触发一次"); this.object.notifyAll(); } }}
输出结果
开始等待Thread-0开始等待Thread-2开始等待Thread-1唤醒所有线程触发一次已经唤醒Thread-1已经唤醒Thread-2已经唤醒Thread-0
自动唤醒线程
带一个参数的wait(long)方法的功能就是等待某一时间类是否有线程对对象进行唤醒,如果超过这个时间则自动唤醒,稍微修改一下程序的代码
public class ThreadA extends Thread { private PublicObject object; public ThreadA(PublicObject object) { this.object = object; } @Override synchronized public void run() { synchronized (this.object) { try { System.out.println("开始等待"+Thread.currentThread().getName()); this.object.wait(1000); System.out.println("已经唤醒"+Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }}
输出结果
开始等待Thread-0开始等待Thread-1开始等待Thread-2已经唤醒Thread-0已经唤醒Thread-1已经唤醒Thread-2唤醒所有线程触发一次