先说例子,再说原理。
等待/通知机制:在某个条件下等待,当条件满足时,通过它结束等待,继续执行。
简单实现:
等待方:
public class Waiter {
public static volatile int flag = 0;
public static void setFlag(int flag) {
Waiter.flag = flag;
}
public static void waitMethod() throws InterruptedException {
System.out.println("进入");
while (flag == 0) {
//避免频繁的执行判断条件
Thread.sleep(1000);
}
System.out.println("因为值被修改所以执行结束");
}
}
通知方:
public class Notifyer {
public static void notifyMethod(int i) {
Waiter.setFlag(i);
}
}
上述等待通知机制是不是很low
1、需要满足volatile的使用条件
2、难以保证及时性
3、CPU资源浪费
Java内置方式:
等待方:
public class Waiter {
private Object lock;
public Waiter(Object lock) {
this.lock=lock;
}
public void waitMethod() throws InterruptedException {
System.out.println("进入");
synchronized (lock) {
lock.wait();
}
System.out.println("因为被唤醒所以执行结束");
}
}
通知方:
public class Notifyer {
public Object lock;
public Notifyer(Object lock) {
this.lock=lock;
}
public void notifyMethod() {
synchronized (object) {
lock.notify();
}
}
}
前提:1、调用wait,notify前必须对对象进行加锁,否则编译可以通过,运行保错java.lang.IllegalMonitorStateException 稍后解释
2、必须先wait(),然后notify()。否则一直wait下去。
在简单实现中,我们做了一些什么?
1、volatile的变量,保证一个线程修改后,另外一个线程可以看到更新后的值
2、sleep 避免频繁执行判断条件
wait,notify做了什么?
1、首先我们没有看到等待条件,以及修改条件的操作
2、其次wait方法执行后会释放锁,执行它的线程进入了waiting状态
3、最后 notify方法执行,使得线程继续执行。
说明wait,notify中隐式的设置了条件,修改了条件,同时提高利用率。
原理:
wait:首先获取锁对象
然后调用了对象的wait()方法,释放锁并进入了对象的监视器的等待队列WaitQuere中。
为什么是进入对象的监视器的等待队列WaitQuere中呢?
答:1、在不加锁的时候反馈的异常是IllegalMonitorStateException,非法的监视器状态。嗯...这应该可以说明一下问题
2、高级玩家阔以去搞搞源码。objectMonitor.hpp
试想一下,如果不加锁,那么如何获取锁对象的监视器,如何进入监视器对象的等待队列。
notify/notifyAll:首先获取锁对象
然后调用对象的notify方法,将监视器对象中WaitQueue队列中等待的线程移到监视器的 SynchronizedQueue队列中。(这一步就相当于是简单实现中的修改条件), 此时线程状态变为阻塞状态。最后释放锁,监视器同步队列中的线程再次获取到锁并从wait()方法返回继续执行(说明notifyAll唤醒是随机的)。
核心:获取监视器,利用监视器的等待队列,同步队列