某天我在***的时候,突然有个小伙伴微信上说:“哥,阿里面试又又挂了,被问到为什么wait()方法要放在同步块中,没答出来!”

我顿时觉得**一紧,仔细回顾一下,如果wait()方法不在同步块中,代码的确会抛出异常:

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

结果是:

Exception in thread "main" java.lang.IllegalMonitorStateExceptionat java.lang.Object.wait(Native Method)at java.lang.Object.wait(Unknown Source)at com.dapeng.day413.WaitSyncBlockTest.test(WaitSyncBlockTest.java:16)at com.dapeng.day413.WaitSyncBlockTest.main(WaitSyncBlockTest.java:40)

但是,为毛呢??我也没去了解过。

机智如我立刻假装正在开会忙得不可开交,回了一条:“开会中,等会和你细说。”

经过一番谷歌之后,找到了答案。

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-1
releaseLock

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

现在来思考一个问题,生产者代码这样修改行不行?

tryLock()
count+1
notify()
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();@Testpublic void test(){try{condition.signal();}catch(Exception e){e.printStackTrace();}}

Exception in thread "main" java.lang.IllegalMonitorStateExceptionat java.lang.Object.wait(Native Method)at java.lang.Object.wait(Unknown Source)at com.dapeng.day413.WaitSyncBlockTest.test(WaitSyncBlockTest.java:16)at com.dapeng.day413.WaitSyncBlockTest.main(WaitSyncBlockTest.java:40)

准确的来说,即便是我们自己在实现自己的锁机制的时候,也应该要确保类似于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设计者为了避免使用者出现lost wake up问题而搞出来的。”

注意演技,一定要轻描淡写中透露着一丝“我其实就知道lost wake up这个名词,再问就要露馅了”的感觉。

于是面试官肯定会追问:“lost wake up问题是什么?”

这个时候你就可以巴啦啦一大堆了。这个过程你要充满自信,表露出那种睥睨天下这种小问题就别来烦我的气概来。

于是,小手一抖,offer到手。

原文转自:https://mp.weixin.qq.com/s/ohcr6T1aB7-lVFJIfyJZjA

转载于:https://www.cnblogs.com/harbin1900/p/10703013.html

阿里面试题,为什么wait()方法要放在同步块中?相关推荐

  1. java 同步块 抛出异常_不把 wait 放在同步块中,为啥这种情况不会抛出 IllegalMonitorStateException?...

    这是一个来自 Java 编程思想的例子,它只是想表达 sleep 的线程可中断,但同步 IO 等待资源,或同步获得锁失败的线程,是不可同步的. //: concurrency/Interrupting ...

  2. [转+整理]十道海量数据处理面试题与十个方法大总结

        海量数据处理:十道面试题与十个海量数据处理方法总结 作者:July.youwang.yanxionglu. 时间:二零一一年三月二十六日 本文之总结:教你如何迅速秒杀掉:99%的海量数据处理面 ...

  3. C语言试题五十五之m个人的成绩存放在score数组中,请编写函数function,它的功能是:将高于平均分的人数作为函数值返回,将高于平均分的分数放在high所指定的数组中。

    1. 题目 m个人的成绩存放在score数组中,请编写函数function,它的功能是:将高于平均分的人数作为函数值返回,将高于平均分的分数放在high所指定的数组中. 2 .温馨提示 C语言试题汇总 ...

  4. C语言试题三十四之求除1到m之内(含m)能北7或11整除的所有整数放在数组a中,通过n返回这些数的个数。

    1. 题目 请编写一个函数function,它的功能是:求除1到m之内(含m)能北7或11整除的所有整数放在数组a中,通过n返回这些数的个数. 2 .温馨提示 C语言试题汇总里可用于计算机二级C语言笔 ...

  5. C语言试题二十九之编写函数int function(int lim,int aa[max])求出小于或等于lim的所有素数并放在aa数组中,该函数返回所求的素数的个数。

    1. 题目 编写函数int function(int lim,int aa[max]),该函数的功能是求出小于或等于lim的所有素数并放在aa数组中,该函数返回所求的素数的个数. 2 .温馨提示 C语 ...

  6. C语言试题十二之m个人的成绩存放在score数组中,请编写函数function,它的功能是:将低于平均分的人数作为函数值返回,将低于平均分的分数放在below所指定的数组中。

    1. 题目 m个人的成绩存放在score数组中,请编写函数function,它的功能是:将低于平均分的人作为函数值返回,将低于平均分的分数放在below所指定的数组中. 2 .温馨提示 C语言试题汇总 ...

  7. python中如何编写代码输入多个数据并把它们放在一个列表中去_编写高质量Python代码的59个有效方法,你用过几个...

    欢迎点击右上角关注小编,除了分享技术文章之外还有很多福利,私信学习资料可以领取包括不限于Python实战演练.PDF电子文档.面试集锦.学习资料等. 这个周末断断续续的阅读完了<Effectiv ...

  8. 雨课堂卷子提前看_雨课堂怎么新建试卷?制作试题的具体方法

    雨课堂是一款受到大家喜爱的教育类软件,大多数用户都会选择这款软件解决学业以及工作上的事情,最近很多用户在准备期初的测试考卷,却不懂得怎么操作,那你知道雨课堂怎么新建试卷的吗?接下来我们一起往下看看制作 ...

  9. 将uni-app打包的H5放在 Android程序中在 webview 显示的两种方法

    将uni-app打包的H5放在 Android程序中在 webview 显示的两种方法 前言 Hash History 完事 前言 之前有个项目要同时做小程序和APP,本着节省时间的想法,研究了下un ...

最新文章

  1. 技术人生:新的生活计划
  2. MyBatis 实际使用案例-编程式使用
  3. [vue] 说说你对Object.defineProperty的理解
  4. LeetCode 124. 二叉树中的最大路径和(DFS)
  5. beeline安装_Hive 系列 之 简介与安装
  6. 各省简称 拼音 缩写_全国所有城市拼音及缩写
  7. 用注册表修改右键菜单
  8. kuangbin带你飞系列目录与简介
  9. discuz 登录代码流程
  10. 网件刷breed_小白爱折腾 篇二:矿渣小娱C1刷breed以安装固件(适用其他路由器)...
  11. 8- 性能测试面试题(测试框架总结)史上最全面试题
  12. 一个广告净化大师拦截视频网站片头广告的实现
  13. 精选优美英文短文1——Dear Basketball(亲爱的篮球)
  14. 在UE4里实现四叉树查找最近点
  15. 微软.Net 社区虚拟大会 -- 首日重点(dotnetConf 2016)
  16. 请问如何修复损坏的jpg文件
  17. ReentrantLock源码分析
  18. mysql清除表数据
  19. 去哪儿网 ReactNative 跨小程序多端方案介绍
  20. Mac程序坞中软件删除出现残留“?”图标无法删除解决方法:

热门文章

  1. 物联网奇点:给物联网设备使用的Docker
  2. 2016百度星资格赛1002 大数相加
  3. 怎样在不处理的情况下在ABBYY FineReader中添加图像
  4. JAVA 的wait(), notify()与synchronized同步机制
  5. 通用权限管理系统组件 (GPM - General Permissions Manager) 中后一个登录的把前一个登录的踢掉功能的实现...
  6. bootloader烧写
  7. grpc使用记录(一) gRPC编译(mscv/gcc)
  8. python之xlrd、xlwt学习
  9. CF1019E Raining season
  10. SourceTree 3.0.17如何跳过注册进行安装? — git图形化工具(一)