zl程序教程

您现在的位置是:首页 >  后端

当前栏目

并发编程— wait 与 notify 为什么是 Object 的成员方法?

方法并发编程 为什么 object 成员 WAIT notify
2023-09-14 09:16:27 时间

转载:https://blog.csdn.net/small_love/article/details/110959097

1、为什么必须和 synchronized 一起使用

在 Java 里面, wait() 和 notify() 是 Object 的成员函数,是基础中的基础。为什么Java 要把 wait() 和 notiry() 放在如此基础的类里面,而不作为 Thread 类的成员函数,或者其他类的函数呢?

在回答这个问题之前,我们先聊聊 为什么 wait() 和 notify() 必须和 synchronized 一起使用?看下面代码:

public class SynchronizedDemo3 {
 
	private Object lock = new Object();
 
	public void increment() {
		synchronized (lock) {
			// do something
			lock.wait();
			// do something
 
		}
	}
 
	public void decrement() {
		synchronized (lock) {
			// do something
			lock.notify();
			// do something
		}
	}
}

 我们启动两个线程 A,B,线程A 调用 increment() 方法,线程B 调用 decrement() 方法。很显然,两个线程之间要通信,对于同一个对象来说,一个线程调用该对象的 wait()方法,另个线程调用该对象的 notify() 方法,该对象本身就要互斥!所有,在调用 wait() 和 notify() 方法之前,需要通过 synchronized 关键字给对象 lock 加锁。

我们都知道,synchronized 关键字可以加在任何对象的成员函数上,任何对象也都可以成为锁。那么,wait() 和 notify() 这样普及,Object 又是所有类的基类,那么 wait() 和 notify() 放在Object 里面最合适不过了。

2、为什么 wait() 的时候必须释放锁

根据上面的代码,当线程A 进入 synchronized(lock)代码块后,也就是对 lock 上了锁,此时,调用wait() 进入阻塞状态,一直不能退出 synchronized 代码块;那么线程B 永远无法进入 synchronized(lock)   代码块里,也就永远没有机会调用 notify(),这岂不就死锁了?

这就涉及一个关键的问题,在wait() 的内部,需要先释放锁lock,然后进入阻塞状态,之后,线程B 可以再持有这个锁,也就能够调用 notify() 方法唤醒线程A,然后,线程A再次 获取锁,继续执行后续代码逻辑,执行完后退出 synchronized 代码块,再次释放锁。

只有如此,才能避免死锁的可能。

3、wait() 与 notify() 的问题

因为 notify() 一次只能唤醒一个线程,如在多生产者-多消费者模式中,如果生产者 想唤醒的是消费者,但有可能唤醒的是生产者, 消费者想唤醒生产者,也有可能把消费者唤醒了。所有在wait() 时最好使用 notifyAll() 唤醒所有的线程,去抢锁。在J.U.C 并发包中 Lock 和Condition 配合可以完美的解决此问题。

4、总结

本篇文章主要介绍了,wait()、notify()和notifyAll() 方法为什么在Object 类中,和在调用 wait()方法后为什么需要释放锁。只要掌握了 synchronized 关键字加锁本质以及线程的互斥与同步的原理,就不难理解这些。