博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
为什么wait()方法要放在同步块
阅读量:6263 次
发布时间:2019-06-22

本文共 1954 字,大约阅读时间需要 6 分钟。

回顾一下,如果wait()方法不在同步块中,代码的确会抛出异常:

public class WaitInSyncBlockTest {    @Test    public void test() {        try {            new Object().wait();        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

结果是:

Exception in thread "main" java.lang.IllegalMonitorStateException    at java.lang.Object.wait(Native Method)    at java.lang.Object.wait(Object.java:503)    at com.kzx.test.juc.WaitInSyncBlockTest.main(WaitInSyncBlockTest.java:7)

 

为什么呢?

Lost Wake-Up Problem

事情得从一个多线程编程里面臭名昭著的问题"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--;releaseLock();

 

注意的是,我这里将两者的两个操作都放进去了同步块中。

终极答案

所以,我们可以总结到,为了避免出现这种lost wake up问题,在这种模型之下,总应该将我们的代码放进去的同步块中。

Java强制我们的wait()/notify()调用必须要在一个同步块中,就是不想让我们在不经意间出现这种lost wake up问题。

不仅仅是这两个方法,包括java.util.concurrent.locks.Condition的await()/signal()也必须要在同步块中。

准确的来说,即便是我们自己在实现自己的锁机制的时候,也应该要确保类似于wait()和notify()这种调用,要在同步块内,防止使用者出现lost wake up问题。

Java的这种检测是很严格的。它要求的是,一定要处于锁对象的同步块中。举例来说:

private Object obj = new Object();private Object anotherObj  = new Object();@Testpublic void produce(){  synchronized(obj){    try{      anotherObj.notify();;    }catch(Exception e){      e.printStackTrace();    }  } }

这样是没有什么卵用的。一样出现IllegalMonitorStateException。

 

转载于:https://www.cnblogs.com/Joy-Hu/p/10693969.html

你可能感兴趣的文章
lzg_ad:使用Virtual PC 部署和测试XP Embedded 发布镜像
查看>>
关于ssh 配置文件的参数说明
查看>>
金山词霸2005无法用鼠标取词
查看>>
Java Http断点下载文件
查看>>
我的微软最有价值专家(Microsoft MVP)之路
查看>>
如何在gcc编译时指定共享库的搜索路径?
查看>>
如何安装和配置 Rex-Ray?- 每天5分钟玩转 Docker 容器技术(74)
查看>>
Linux下SENDMAIL+OPENWEBMAIL(1)
查看>>
无法添加内核模式驱动的打印机
查看>>
Spring Cloud规范实战
查看>>
javascript event 事件
查看>>
2012自学CCNP路由与交换之三网络设备造型及验收
查看>>
lf4j+logback配置方式,logback.groovy使用备忘
查看>>
RHEL,centOS下vncserver,service命令关联的rpm包
查看>>
slf4j+logback配置方式,logback.groovy使用备忘
查看>>
android中阿拉伯文研究
查看>>
SCCM TP4安装客户端Agent
查看>>
配置PIX515E DMZ的基本方法与故障排除
查看>>
HA Cluster—ansible批量操作
查看>>
SpringMVC那点事
查看>>