并发编程— wait 与 notify 为什么是 Object 的成员方法?
转载: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 关键字加锁本质以及线程的互斥与同步的原理,就不难理解这些。
相关文章
- C#基础视频教程3.2 常见控件类型和使用方法
- PHP格式化数字和SMARTY格式化数字的方法
- 【IOS-IAP防护】验证用户付费收据!拒绝IAP CRACKER!拒绝IAP FREE!让IPHONE越狱用户无从下手!【2012年5月2日更新防护IAP FREE的方法】
- 高并发系统限流方法
- 如何控制某个方法允许并发访问线程的个数?
- 【Java】java使用反射访问对象方法和成员变量
- ArcEngine数据删除几种方法和性能比较
- 线程同步方法和差别~(高并发中多个线程访问统一资源域,容易出现线程安全性)
- ML之ME/LF:机器学习之风控业务中常用模型监控指标CSI(特征稳定性指标)的简介、使用方法、案例应用之详细攻略
- Python语言学习:Python语言学习之正则表达式常用函数之re.search方法【输出仅一个匹配结果(内容+位置)】、re.findall方法【输出所有匹配结果(内容)】案例集合之详细攻略
- Py之gym:gym的简介、安装、使用方法之详细攻略
- 你真的明白关于迭代器的方法、使用异常、并发修改异常介绍嘛?
- 一文抽丝剥茧带你掌握复杂Gremlin查询的调试方法
- 实战解读丨Linux下实现高并发socket最大连接数的配置方法
- 【高并发】又一个朋友面试栽在了Thread类的stop()方法和interrupt()方法上!
- 【Java并发编程实战】—–“J.U.C”:ReentrantLock之二lock方法分析
- 一遇“高并发”系统就难逃一“崩”,性能测试的方法你选对了吗?(下)
- java===java基础学习(12)---方法的重写和重载
- golang_并发安全: slice和map并发不安全及解决方法
- openeuler-ssh容器镜像使用方法
- vue或uniapp中文本框输入数字固定几位小数或只能输入整数的方法