目录
为什么 wait 方法必须在 synchronized 保护的同步代码中使用?
为什么 wait/notify/notifyAll 被定义在 Object 类中,而 sleep 定义在 Thread 类中?
wait/notify 和 sleep 方法的异同?
例如:生产者消费者模型
class BlockingQueue { Queue<String> buffer = new LinkedList<String>(); public void give(String data) { buffer.add(data); notify(); // Since someone may be waiting in take } public String take() throws InterruptedException { while (buffer.isEmpty()) { wait(); } return buffer.remove(); } }notify 方法可能会在 buffer.isEmpty 和 wait 方法之间被调用,如下:
首先,消费者线程调用 take 方法并判断 buffer.isEmpty 方法是否返回 true,若为 true 代表buffer是空的,则线程希望进入等待,但是在线程调用 wait 方法之前,就被调度器暂停了,所以此时还没来得及执行 wait 方法。此时生产者开始运行,执行了整个 give 方法,它往 buffer 中添加了数据,并执行了 notify 方法,但 notify 并没有任何效果,因为消费者线程的 wait 方法没来得及执行,所以没有线程在等待被唤醒。此时,刚才被调度器暂停的消费者线程回来继续执行 wait 方法并进入了等待。正确代码:
public void give(String data) { synchronized (this) { buffer.add(data); notify(); } } public String take() throws InterruptedException { synchronized (this) { while (buffer.isEmpty()) { wait(); } return buffer.remove(); } }wait 方法会释放 monitor 锁,这也要求我们必须首先进入到 synchronized 内持有这把锁。
“虚假唤醒”(spurious wakeup)的问题,线程可能在既没有被notify/notifyAll,也没有被中断或者超时的情况。while里面的条件,如果不满足条件,就会继续wait,也就消除了虚假唤醒的风险。
wait使用格式:
synchronized (obj) { while (condition does not hold) obj.wait(); ... // Perform action appropriate to condition }主要有两点原因:
因为 Java 中每个对象都有一把称之为 monitor 监视器的锁,由于每个对象都可以上锁,这就要求在对象头中有一个用来保存锁信息的位置。这个锁是对象级别的,而非线程级别的,wait/notify/notifyAll 也都是锁级别的操作,它们的锁属于对象,所以把它们定义在 Object 类中是最合适,因为 Object 类是所有对象的父类。因为如果把 wait/notify/notifyAll 方法定义在 Thread 类中,会带来很大的局限性,比如一个线程可能持有多把锁,以便实现相互配合的复杂逻辑,假设此时 wait 方法定义在 Thread 类中,如何实现让一个线程持有多把锁呢?又如何明确线程等待的是哪把锁呢?既然我们是让当前线程去等待某个对象的锁,自然应该通过操作对象来实现,而不是操作线程。相同点:
它们都可以让线程阻塞。它们都可以响应 interrupt 中断:在等待的过程中如果收到中断信号,都可以进行响应,并抛出 InterruptedException 异常。但是它们也有很多的不同点:
wait 方法必须在 synchronized 保护的代码中使用,而 sleep 方法并没有这个要求。在同步代码中执行 sleep 方法时,并不会释放 monitor 锁,但执行 wait 方法时会主动释放 monitor 锁。sleep 方法中会要求必须定义一个时间,时间到期后会主动恢复,而对于没有参数的 wait 方法而言,意味着永久等待,直到被中断或被唤醒才能恢复,它并不会主动恢复。wait/notify 是 Object 类的方法,而 sleep 是 Thread 类的方法。以上就是关于 wait/notify 与 sleep 的异同点。