问题起源

事情得从一个多线程编程里面臭名昭著的问题"Lost wake-up problem"说起。

这个问题并不是说只在Java语言中会出现,而是会在所有的多线程环境下出现。

假如我们有两个线程,一个消费者线程,一个生产者线程。生产者线程的任务可以简化成将count加一,而后唤醒消费者;消费者则是将count减一,而后在减到0的时候陷入睡眠,代码如下:

生产者伪代码:

count+1;
notify();

消费者伪代码:

while(count<=0)
wait()
count--

熟悉多线程的朋友一眼就能够看出来,这里面有问题。什么问题呢?

生产者是两个步骤:

  1. count+1;
  2. notify();

消费者也是两个步骤:

  1. 检查count值;
  2. 睡眠或者减一;
  万一这些步骤混杂在一起呢?比如说,初始的时候count等于0,这个时候消费者检查count的值,发现count小于等于0的条件成立;就在这个时候,发生了上下文切换,生产者进来了,噼噼啪啪一顿操作,把两个步骤都执行完了,也就是发出了通知,准备唤醒一个线程。这个时候消费者刚决定睡觉,还没睡呢,所以这个上面唤醒通知就会被丢掉。紧接着,消费者就睡过去了……

这就是所谓的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 问题)的更多相关文章

  1. 阿里面试题,为什么wait()方法要放在同步块中?

    某天我在***的时候,突然有个小伙伴微信上说:“哥,阿里面试又又挂了,被问到为什么wait()方法要放在同步块中,没答出来!” 我顿时觉得**一紧,仔细回顾一下,如果wait()方法不在同步块中,代码 ...

  2. 为什么 wait 和 notify 方法要在同步块中调用?

    Java API 强制要求这样做,如果你不这么做,你的代码会抛出 IllegalMonitorStateException 异常.还有一个原因是为了避免 wait 和 notify 之间产生竞态条件.

  3. 为什么 wait()方法和 notify()/notifyAll()方法要在同步块 中被调用 ?

    这是 JDK 强制的,wait()方法和 notify()/notifyAll()方法在调用前都必须先获得对 象的锁

  4. 为什么wait()方法要放在同步块

    回顾一下,如果wait()方法不在同步块中,代码的确会抛出异常: public class WaitInSyncBlockTest { @Test public void test() { try { ...

  5. Java并发编程(六)-- 同步块

    上一节已经讲到,使用Synchronzied代码块可以解决共享对象的竞争问题,其实还有其他的方法也可以避免资源竞争问题,我统称他们为Java同步块.Java 同步块(synchronized bloc ...

  6. 为什么WAIT必须在同步块中

    我们知道java的Object有wait和notify方法,如果要使用wait和notify的话,那么必须在synchronized块中,否则会抛出IllegalMonitorStateExcepti ...

  7. 为什么 wait(), notify()和 notifyAll ()必须在同步方法或 者同步块中被调用?

    当一个线程需要调用对象的 wait()方法的时候,这个线程必须拥有该对象的锁,接 着它就会释放这个对象锁并进入等待状态直到其他线程调用这个对象上的 notify() 方法.同样的,当一个线程需要调用对 ...

  8. jquery中Live方法不可用,Jquery中Live方法失效

    jquery中Live方法不可用,Jquery中Live方法失效 >>>>>>>>>>>>>>>>> ...

  9. Properties集合中的方法store和Properties集合中的方法load

    Properties集合中的方法store public class Demo01Properties { public static void main(String[] args) throws ...

随机推荐

  1. web安全之跨站请求伪造

    CSRF(Cross-site request forgery),中文名称:跨站请求伪造.因为这个不是用户真正想发出的请求,这就是所谓的请求伪造;因为这些请求也是可以从第三方网站提交的,所以前缀跨站二 ...

  2. HTML中的align和valign这两个属性

    转自:https://www.douban.com/note/325833958/ align和valign属性均是规定表格相对于周围元素的对齐方式,区别就在于: 1.align属性趋向于左右对齐,其 ...

  3. HDU-5540 Secrete Master Plan

    Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission( ...

  4. 任务24:WebHost的配置

    24 任务24:WebHost的配置 创建HelloCore的项目 我们新建一个空的mvc项目 我们在这里调用COnfigureAppConfiguration方法更改默认的配置.为读取setting ...

  5. Hibernate3--快速入门--第一天

    1. Hibernate概述 Hibernate是轻量级JavaEE应用的持久层解决方案,是一个关系数据库ORM框架. a. 轻量级: 使用方便 (比Apache DbUtils 复杂很多倍 )这个概 ...

  6. (水题)洛谷 - P1553 - 数字反转(升级版) - 字符串格式转换

    https://www.luogu.org/problemnew/show/P1553 忘记给整数加上前导零去除的代码了.其实不去也可以,额外的进位用一个carry另外存起来就好. #include& ...

  7. 黑客攻防技术宝典web实战篇:核心防御机制习题

    猫宁!!! 参考链接:http://www.ituring.com.cn/book/885 黑客攻防技术宝典web实战篇是一本非常不错的书,它的著作人之一是burpsuite的作者,课后的习题值得关注 ...

  8. Luogu P2278 [HNOI2003]操作系统【优先队列/重载运算符/模拟】 By cellur925

    题目传送门 本来是照着二叉堆的题去做的...没想到捡了个模拟...不过模拟我都不会...我好弱啊... 其实核心代码并不长,比辰哥的标程短到不知哪里去...但是思路需要清晰. 读题的时候我明白,当有优 ...

  9. Luogu P1754球迷购票问题【dp/卡特兰数】By cellur925

    题目传送门 虽然是水dp,但我感到还是有些无从下手== f[i][j]表示还剩i个50元没考虑,j个100元没考虑的方案数,可有转移f[i][j]=f[i-1][j]+f[i][j-1] 但其实它也可 ...

  10. 一起学Android之Activity

    概述 本文以一个简单的小例子,简述Android开发中Activity的相关知识,仅供学习分享使用. 什么是Activity? Activity是一个应用程序组件,通常显示为一个页面,用户可以通过Ac ...