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

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

我顿时以为**一紧,仔细回顾一下,若是wait()方法不在同步块中,代码的确会抛出异常:面试

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(Unknown Source)
    at com.dapeng.day413.WaitSyncBlockTest.test(WaitSyncBlockTest.java:16)
    at com.dapeng.day413.WaitSyncBlockTest.main(WaitSyncBlockTest.java:40)

可是,为毛呢??我也没去了解过。微信

机智如我马上伪装正在开会忙得不可开交,回了一条:“开会中,等会和你细说。”多线程

通过一番谷歌以后,找到了答案。spa

Lost Wake-Up Problem

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

这个问题并非说只在Java语言中会出现,而是会在全部的多线程环境下出现。设计

假若有两个线程,一个消费者线程,一个生产者线程。生产者线程的任务能够简化成将count加一,然后唤醒消费者;消费者则是将count减一,然后在减到0的时候陷入睡眠:code

生产者伪代码:对象

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();

    @Test
    public void test()

    {

        try{
            condition.signal();

        }catch(Exception e){
            e.printStackTrace();

        }

    }
Exception in thread "main" java.lang.IllegalMonitorStateException
    at 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

相关文章
相关标签/搜索