这段代码大多数情况下运行正常,但是某些情况下会出问题。什么时候会出现什么问题?如何修正?可见博客 http://yueyemaitian.iteye.com/blog/1387901

1.public class MyStack {

2.    private List<String> list = new ArrayList<String>();

3.

4.    public synchronized void push(String value) {

5.        synchronized (this) {

6.            list.add(value);

7.            notify();

8.        }

9.    }

10.

11.    public synchronized String pop() throws InterruptedException {

12.        synchronized (this) {

13.            if (list.size() <= 0) {

14.                wait();

15.            }

16.            return list.remove(list.size() - 1);

17.        }

18.    }

19.}

下面是关于这道题的分析:

list.remove(list.size() - 1);这句代码有可能引发数组下标越界  原因: 假设其中一种情形呵!出问题的情形可能很多,但原理都差不多。下面的标号代表程序时序的先后顺序。  1,初始化时list的值为0,然后线程1调用了pop,于是被wait了,然后释放了锁。  2,线程2调用push,在notify之前有线程3调用pop(记住这时候线程1还没有被唤醒,还在wait住),此时线程3会因为等待锁而挂起,或自旋,反正就是在等待锁可用。

3,然后线程2继续往下执行,notify被执行(但这时候线程1是不会唤醒的,因为锁还在线程2占用),线程2退出push方法,释放内置锁,此时,线程1和线程3都在内置锁等待队列里面。由于synchronized是没法保证线程竞争的公平性,所以线程1和线程3都可能得到锁。  4,假设线程1竞争到了锁,不会出问题,正常去除list值,然后remove,执行完后线程3执行,同样被wait住。  5,假设线程3竞争到了锁,问题来了,线程3会判断到list的size不为0,于是remove,所以list的size就为0了,然后线程 3释放锁,这时候,线程1就得到锁,于是从wait中醒来,继续执行,然后直接调用list的remove,由于list的size=0,那么remove(-1),越界错误就产生了。

还有同学说两个线程都在wait处等候也会出问题,其实不会出问题的,因为是调用的notify而不是notifyAll,如果是调用notifyAll那么也会出同样的问题。

至于改进:

看到这个题目我就很纳闷,为什么要用双重锁,好像没有必要双重锁。我第一眼看到双重锁的时候就在想,出题者是不是在模拟一个套管死锁,我也确实为找这个死锁付出了一些时间。但是这个双重检查都是可重入的锁,都是对于this对象上的锁。所以不存在套管死锁。

改进1,——最小代码改动,就在remove之前再检查list.size==0

改进2,——去掉push和pop方法内的第二重锁检查,我确实没有发现这个锁会有什么用,反而耗性能。 当然这里还是要有方案1的判断(谢谢一楼提醒)。

改进3,——重新设计,如果是我来设计这么一个生产者,消费者模式。我更愿意用LinkedBlockingQueue,它有take方法阻塞消费者直到队列可用。而且还有offer方法阻塞生产者直到队列可以插入,可以有效的阻止OOM。

这个题目出的好,难道是阿里有人犯过这个错误!呵呵!

关于本题的讨论如有任何纰漏,请大家及时指出呵!

最新文章

  1. ASP.NET 导出数据表格
  2. GridView不能添加头布局,并且scrollView与GridView冲突导致一些页面无法融合
  3. Sprint第三个冲刺(第四天)
  4. WinForm AutoComplete 输入提示、自动补全
  5. MySQL语句45道练习题及答案
  6. Apache Kafka 分布式消息队列中间件安装与配置 转载
  7. linux下LAMP环境搭建尝试
  8. windowSoftInputMode属性详解
  9. 记录jpcap在Ubuntu&amp;Window下的配置过程
  10. MAC X OS 自定义分辨率
  11. Android UI 学习 自定义的布局 平滑移动 VelocityTracker()
  12. CentOS 6 安装Oracle11g
  13. 《Windows驱动开发技术详解》之HelloDDK
  14. 如何在Eclipse下安装myeclipse插件
  15. Webstorm 提示 Can&#39;t use Subversion command line client
  16. Java Web Session设置
  17. [ Java面试题 ] 框架篇
  18. JavaSE_坚持读源码_Object对象_Java1.7
  19. Android的Device File Explorer刷新文件
  20. 小程序学习笔记三:页面文件详解之视图层WXML、WXS、WXSS文件

热门文章

  1. [uva11991]map和vector的入门
  2. 【bzoj3362-导航难题】带权并查集
  3. 【BZOJ】1500: [NOI2005]维修数列
  4. 【51NOD】1201 整数划分
  5. 2009 Round2 A Crazy Rows (模拟)
  6. ASP.NET 设置DropDownList的当前选项
  7. js中的indexOf
  8. 一种面向云服务的UCON多义务访问控制方法及系统
  9. 在linux程序里面,知道一个函数地址,改函数是属于某个动态库的,怎么样得到这个动态库的全【转】
  10. 【openjudge】C15C Rabbit&#39;s Festival CDQ分治+并查集