Java中wait()方法为什么要放在同步块中?(lost wake-up 问题)
问题起源
事情得从一个多线程编程里面臭名昭著的问题"Lost wake-up problem"说起。
这个问题并不是说只在Java语言中会出现,而是会在所有的多线程环境下出现。
假如我们有两个线程,一个消费者线程,一个生产者线程。生产者线程的任务可以简化成将count加一,而后唤醒消费者;消费者则是将count减一,而后在减到0的时候陷入睡眠,代码如下:
生产者伪代码:
count+1;
notify();
消费者伪代码:
while(count<=0)
wait()
count--
熟悉多线程的朋友一眼就能够看出来,这里面有问题。什么问题呢?
生产者是两个步骤:
- count+1;
- notify();
消费者也是两个步骤:
- 检查count值;
- 睡眠或者减一;

这就是所谓的lost wake up问题。(丢掉了唤醒线程的那条信息)
那么怎么解决这个问题呢?
现在我们应该就能够看到,问题的根源在于,消费者在检查count到调用wait()之间,count就可能被改掉了。这就是一种很常见的竞态条件。很自然的想法是,让消费者和生产者竞争一把锁,竞争到了的,才能够修改count的值。
于是生产者的代码是:
tryLock()
count+1
notify()
releaseLock()
消费者的代码是:
tryLock()
while(count <= 0)
wait()
count-1
releaseLock
注意的是,我这里将两者的两个操作都放进去了同步块中。现在来思考一个问题,生产者代码这样修改行不行?
答案是,这样改毫无卵用,依旧会出现lost wake up问题,而且和无锁的表现是一样的。
终极答案
所以,我们可以总结到,为了避免出现这种lost wake up问题,在这种模型之下,总应该将我们的代码放进去的同步块中。
Java强制我们的wait()/notify()调用必须要在一个同步块中(不然的话会报错),就是不想让我们在不经意间出现这种lost wake up问题。
不仅仅是这两个方法,包括java.util.concurrent.locks.Condition的await()/signal()也必须要在同步块中:
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
@Test
public void test() {
try {
condition.signal();
} catch (Exception e) {
e.printStackTrace();
}
}

确的来说,即便是我们自己在实现自己的锁机制的时候,也应该要确保类似于wait()和notify()这种调用,要在同步块内,防止使用者出现lost wake up问题。
Java的这种检测是很严格的。它要求的是,一定要处于相同锁对象的同步块中。举例当不同锁对象的时候来说:
private Object obj = new Object();
private Object anotherObj = new Object();
@Test
public void produce() {
synchronized (obj) {
try {
anotherObj.notify();
} catch (Exception e) {
e.printStackTrace();
}
}
}
这样是没有什么卵用的。一样出现IllegalMonitorStateException。
所以大家知道该怎么做了吧?哈哈
Java中wait()方法为什么要放在同步块中?(lost wake-up 问题)的更多相关文章
- 阿里面试题,为什么wait()方法要放在同步块中?
某天我在***的时候,突然有个小伙伴微信上说:“哥,阿里面试又又挂了,被问到为什么wait()方法要放在同步块中,没答出来!” 我顿时觉得**一紧,仔细回顾一下,如果wait()方法不在同步块中,代码 ...
- 为什么 wait 和 notify 方法要在同步块中调用?
Java API 强制要求这样做,如果你不这么做,你的代码会抛出 IllegalMonitorStateException 异常.还有一个原因是为了避免 wait 和 notify 之间产生竞态条件.
- 为什么 wait()方法和 notify()/notifyAll()方法要在同步块 中被调用 ?
这是 JDK 强制的,wait()方法和 notify()/notifyAll()方法在调用前都必须先获得对 象的锁
- 为什么wait()方法要放在同步块
回顾一下,如果wait()方法不在同步块中,代码的确会抛出异常: public class WaitInSyncBlockTest { @Test public void test() { try { ...
- Java并发编程(六)-- 同步块
上一节已经讲到,使用Synchronzied代码块可以解决共享对象的竞争问题,其实还有其他的方法也可以避免资源竞争问题,我统称他们为Java同步块.Java 同步块(synchronized bloc ...
- 为什么WAIT必须在同步块中
我们知道java的Object有wait和notify方法,如果要使用wait和notify的话,那么必须在synchronized块中,否则会抛出IllegalMonitorStateExcepti ...
- 为什么 wait(), notify()和 notifyAll ()必须在同步方法或 者同步块中被调用?
当一个线程需要调用对象的 wait()方法的时候,这个线程必须拥有该对象的锁,接 着它就会释放这个对象锁并进入等待状态直到其他线程调用这个对象上的 notify() 方法.同样的,当一个线程需要调用对 ...
- jquery中Live方法不可用,Jquery中Live方法失效
jquery中Live方法不可用,Jquery中Live方法失效 >>>>>>>>>>>>>>>>> ...
- Properties集合中的方法store和Properties集合中的方法load
Properties集合中的方法store public class Demo01Properties { public static void main(String[] args) throws ...
随机推荐
- vue中minxin---小记
定义全局的方法,例如定义过滤器,在很多地方都会用到,就可以定义在minxin中 demo: 数据格式化 保留指定的小数位数 var mixin={ filters:{ fixedNum:functio ...
- 关于Android ListView组件中android:drawSelectorOnTop含义(转载)
转自:http://yangguangfu.iteye.com/blog/902559 When set to true, the selector will be drawn over the se ...
- bzoj 1385: [Baltic2000]Division expression【脑洞】
加括号再去括号就是除变加,显然尽可能多的除变加是最优的,然后发现唯一不能变成乘数的是第二个数,所以把其他数乘起来mod第二个数,如果是0就是YES,否则说明最后不能除尽,就是NO #include&l ...
- IT兄弟连 JavaWeb教程 Servlet转发
Servlet对象由Servlet容器创建,并且Servlet对象的service()方法也由容器调用,一个Servlet对象可否直接调用另一个Servlet对象的service()方法呢?答案是否定 ...
- K8S学习心得 == kube-controller-manager 报错configmaps "extension-apiserver-authentication" is forbidden: User "kubernetes" cannot get resource "configmaps" in API group ""
当我按照教材设置证书,配置好kube-controller的相关条件后,启动kube-controller-manage组件,却意外报错. 一.基本信息如下: 1. kube-controller-m ...
- 运行scrapyd报错
转自: https://blog.csdn.net/qq_29719097/article/details/89431234 web.Server Traceback (most recent ...
- vlc media player
还是很好用的目前来看 倍速播放: [ 减速播放 ] 加速播放 = 恢复原速度
- [WOJ2549]逻辑的连通性
题目描述: 数学中,假如有命题 p 一定能推出命题 q,则称 p 是 q 的充分条件,q 是 p 的必要 条件. 特别的,当 p 既是 q 的充分条件,又是 q 的必要条件时,称 p 和 q 互为 充 ...
- Mirror Number SPOJ - MYQ10
Mirror Number SPOJ - MYQ10 题意:http://blog.csdn.net/hcbbt/article/details/38349367 稍微改一下http://www.cn ...
- Eclipse的ant调用maven
需要在 eclipse 的 windows - preferences - ant - runtime - classpath - global entries 加入 eclipse 里面的 jsch ...