• 一道阿里多线程面试题分析


    首先,来看看这个面试题目吧。

    题目来源:   http://www.linuxidc.com/Linux/2014-03/98715.htm

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

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

    可以看出,MyStack主要实现入栈出栈功能,ArrayList不是线程安全的类,因此程序中用synchronized关键字来保证线程安全。大多数情况下,都能正确运行,但是在特殊情况下会出现一些意外。

           tips:从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。相应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。

    case1:删除不存在的元素

          假设现在有三个线程A、B、C,其中A用于添加元素,B、C用于删除元素。

          某时刻,栈为空,

          step1、线程B运行,获取锁,list.size()=0,进入wait(),wait状态下会释放当前锁

          step2、线程A运行,获取锁,添加元素,执行list.add(value),此时list.size()=1,注意:在A执行notify()之前,线程C启动,发现其他线程已经拥有对象锁,因此进入阻塞状态,等待锁

         step3、线程A执行notify(),试图唤醒等待中的线程B,但是但是但是,如果此时C获取了对象锁,那么将优先执行,那么C判断list.size()=1,直接删除元素,然后释放对象锁

         step4、wait状态下的B获取对象锁,直接执行list.remove(list.size()-1),发生错误!!!

        解决办法: 使用可同步的数据结构来存放数据,比如LinkedBlockingQueue之类。由这些同步的数据结构来完成繁琐的同步操作。

    case2:虚假唤醒

        虚假唤醒就是一些obj.wait()会在除了obj.notify()和obj.notifyAll()的其他情况被唤醒,而此时是不应该唤醒的。

        解决的办法是基于while来反复判断进入正常操作的临界条件是否满足: (将if换成while)

            synchronized (obj) {  
                while (<condition does not hold>)  
                    obj.wait();  
                ... // Perform action appropriate to condition  
            } 


  • 相关阅读:
    搭建VirtoCommerce2.6开发环境,同官方dev分支保持同步(上)
    VirtoCommerce中文博客站,2015年11月18日上线了!
    C#编程连接数据库,通过更改配置文件切换数据库功能。
    浅谈dataGridView使用,以及画面布局使用属性,对datagridview进行增删改查操作,以及委托使用技巧
    将listBox中信息显示在dataGridview中,操作datagridview后删除listBox信息和SQL数据库信息 续(浅谈listBox..)
    for语句嵌套使用 实现9*9乘法表
    浅谈ListBox控件,将对象封装在listBox中,在ListBox中显示对象中某个属性,在ListBox中移除和移动信息
    使用SignalR为FineUI/Webform打造消息总线
    封装一个错误重试类
    封装一个锁处理类
  • 原文地址:https://www.cnblogs.com/wennian/p/5036887.html
Copyright © 2020-2023  润新知