美文网首页Java 杂谈Java学习笔记
线程通信中的虚假唤醒

线程通信中的虚假唤醒

作者: 蜗先生 | 来源:发表于2017-06-03 20:57 被阅读61次

今天做了一个关于线程通信的虚假唤醒题。做的时候没想到,对此也不是很熟悉,查过资料才知道原因,在此自己归纳总结一遍。

问题: 这段代码大多数情况下运行正常,但是某些情况下会出问题。什么时候会出现什么问题?如何修正?

    public class MyStack { 
        private List<String> list = new ArrayList<String>(); 
        public synchronized void push(String value) { 
            synchronized (this) { 
                list.add(value); 
                notify(); 
            } 
        } 
        public synchronized String pop() throws InterruptedException { 
            synchronized (this) { 
                if (list.size() <= 0) { 
                    wait(); 
                } 
                return list.remove(list.size() - 1); 
            } 
        } 
    } 

个人觉得这个题难就难在没有给run和main方法,发散思维,就如同我写一个方法,只有一条语句System.out,println("Hello,world!"),然后问你这段代码大多数情况下运行正常,但是某些情况下会出现问题,什么时候会出现问题?如何修改?。。。是否是无稽之谈。如果该面试题给出run和main方法,分分钟就找出来,就算不知道虚假唤醒这么回事,找到问题所在,也能想办法解决。
当然这个题的意义就是了解虚假唤醒,也就是不该唤醒的时候唤醒导致线程不安全了。

根据题意分析:
什么时候会出现什么问题?
假设有三个线程A、B、C, A线程执行push方法,B、C线程执行pop方法
首先B线程先执行pop,list中无数据,所以B线程wait进入阻塞状态,然后A线程执行push增加数据,执行完notify()唤醒线程B并且释放该锁。此时就绪队列中有线程B和线程C。再假设线程C先执行,因为list中有数据,所以跳过if执行remove方法,然后线程B执行,线程B直接在wait的下一行执行,没有再判断list中是否有数据就执行了remove方法,而此时list中没有数据,所以出现了越界异常。

注:同步锁锁住的对象的代码就相当于房子,同步锁就是房门的一把锁,线程C从房门开锁进去再锁上没有任何问题,但是线程B由等待到唤醒就相当于房子里有一个地下通道,那样就算房门有钥匙(就是有同步锁)也阻挡不了线程B进入房内执行后面的代码了。

知道了出现的问题是由于唤醒之后没有在此判断list中是否有数据引起的线程不安全,就是虚假唤醒。下面是解决办法:
如何修改?
1.把pop中的if判断改成while判断或者使用线程安全的数据结构存储数据

既然要在唤醒之后再次判断list中是否有数据,那么就用while来判断,没有数据则等待,反之跳出while循环执行remove方法。
2.synchronized修饰的方法和块重复,去掉锁块

就像之前说的,synchronized就是房门的锁,题中synchronized修饰的方法,锁对象是this,锁块的对象也是this,就像一个房门上了两把相同的锁,多此一举,没有作用。

相关文章

  • 线程通信中的虚假唤醒

    今天做了一个关于线程通信的虚假唤醒题。做的时候没想到,对此也不是很熟悉,查过资料才知道原因,在此自己归纳总结一遍。...

  • 线程虚假唤醒

    一般而言线程调用wait()方法后,需要其他线程调用notify,notifyAll方法后,线程才会从wait方法...

  • 线程虚假唤醒的Java演示

    什么是线程虚假唤醒 在不同的语言,甚至不同的操作系统上,条件锁都会产生虚假唤醒现象。所有语言的条件锁库都推荐用户把...

  • 虚假唤醒

    原创文章,转载请注明原文章地址,谢谢! 生产者消费者案例 我们先用经典的生产者消费者案例来引出问题。 测试结果 通...

  • 自旋锁在高并发的用处,实现各种锁

    虚假唤醒:由于莫名其妙的原因,线程有可能在没有调用过notify()和notifyAll()的情况下醒来。这就是所...

  • Java多线程中的虚假唤醒和如何避免

    先来看一个例子 一个卖面的面馆,有一个做面的厨师和一个吃面的食客,需要保证,厨师做一碗面,食客吃一碗面,不能一次性...

  • Futex 初学之 虚假唤醒

    用while 代替 if ,防止虚假唤醒。 while( valueneed to wait == true){ ...

  • 虚假唤醒(spurious wakeup)

    On a multi-processor, it may be impossible for an impleme...

  • 2020-08-03如何实现Java线程的 阻塞/唤醒(和暂停/

    如何实现Java线程的 阻塞/唤醒(和暂停/继续 类似 以下为线程 阻塞/唤醒 主要代码 如何使用?

  • 线程核心方法-wait

    ①-1、代码演示使用Object的Notify唤醒wait的线程 ①-2、代码演示使用定时等待唤醒wait的线程 ...

网友评论

    本文标题:线程通信中的虚假唤醒

    本文链接:https://www.haomeiwen.com/subject/fipmfxtx.html