这段代码大多数情况下运行正常,但是某些情况下会出问题。什么时候会出现什么问题?如何修正?可见博客 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。

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

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

java-多线程-一道阿里面试题分析的更多相关文章

  1. Java多线程——线程八锁案例分析

    Java多线程——线程八锁案例分析 摘要:本文主要学习了多线程并发中的一些案例. 部分内容来自以下博客: https://blog.csdn.net/dyt443733328/article/deta ...

  2. 面试大厂必看!就凭借这份Java多线程和并发面试题,我拿到了字节和美团的offer!

    最近好多粉丝私信我说在最近的面试中老是被问到多线程和高并发的问题,又对这一块不是很了解,很简单就被面试官给问倒了,被问倒的后果当然就是被刷下去了,因为粉丝要求,我最近也是花了两天时间 给大家整理了这一 ...

  3. Java多线程与并发面试题

    1,什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速.比如,如果一个线程完成一 ...

  4. java多线程——线程池源码分析(一)

    本文首发于cdream的个人博客,点击获得更好的阅读体验! 欢迎转载,转载请注明出处. 通常应用多线程技术时,我们并不会直接创建一个线程,因为系统启动一个新线程的成本是比较高的,涉及与操作系统的交互, ...

  5. java多线程----线程池源码分析

    http://www.cnblogs.com/skywang12345/p/3509954.html 线程池示例 在分析线程池之前,先看一个简单的线程池示例. 1 import java.util.c ...

  6. JAVA多线程高并发面试题总结

    ReadMe : 括号里的内容为补充或解释说明. 多线程和高并发是毕业后求职大厂面试中必问的知识点,自己之前总是面试前才去找相关的知识点面试题来背背,隔段时间又忘了,没有沉淀下来,于是自己总结了下相关 ...

  7. 那些面试官必问的JAVA多线程和并发面试题及回答

    Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用.而线程是在进程中执行的一个任务.Java运行环 ...

  8. JAVA多线程学习十七 - 面试题

    前面针对多线程相关知识点进行了学习,那么我们来来看看常见的面试题: 1. 空中网面试题1 package com.kongzhongwang.interview; import java.util.c ...

  9. java多线程的一些面试题

    8.callable与fature Callable与Runnable类似,但是Callable有返回值,并且有一个参数化的类型. Fature保存异步计算的结果.9.执行器 Executor.10. ...

随机推荐

  1. 【51NOD-0】1081 子段求和

    [算法]树状数组(区间和) [题解]记得开long long #include<cstdio> #include<cstring> #include<algorithm& ...

  2. Optimal Milking(POJ2112+二分+Dinic)

    题目链接:http://poj.org/problem?id=2112 题目: 题意:有k台挤奶机,c头奶牛,每台挤奶机每天最多生产m的奶,给你每个物品到其他物品的距离(除了物品到自己本省的距离为0外 ...

  3. java map 转 json 自编封装

    1.自编封装代码: import com.alibaba.fastjson.JSON; import java.util.*; public class jsonConversion { privat ...

  4. PHP序列化、反序列化常用的魔术方法

    __wakeup() //使用unserialize时触发__sleep() //使用serialize时触发__destruct() //对象被销毁时触发__call() //在对象上下文中调用不可 ...

  5. 树莓派开启smb

    1.安装smb apt-get install samba samba-common-bin 2.修改/etc/samba/smb.conf配置 设置使用系统用户登入 增加smb访问文件夹 [shar ...

  6. 微信支付:curl 出错,错误码: 60

    在测试微信支付 - 模式二 - 扫码支付时,遇到如下错误: curl出错,错误码:60 这是因为在 WxPay.Api.php 文件中做了严格的 ssl 证书校验: curl_setopt($ch,C ...

  7. Style2Paints:用AI技术为线稿快速上色的工具(GitHub 3310颗星)

    python 开源项目: Style2Paints:用AI技术为线稿快速上色的工具(GitHub 3310颗星) https://github.com/lllyasviel/style2paints

  8. selenium===requestium模块介绍

    有时,你可能会在网上实现一些自动化操作.比如抓取网站,进行应用测试,或在网上填表,但又不想使用API,这时自动化就变得很必要.Python提供了非常优秀的Requests库可以辅助进行这些操作.可惜, ...

  9. EasyHook远程进程注入并hook api的实现

    EasyHook远程进程注入并hook api的实现 http://blog.csdn.net/v6543210/article/details/44276155

  10. 【bzoj2006】NOI2010超级钢琴

    补了下前置技能…… 题意就是求一段区间的权值和前k大的子序列的和. 把段扔进优先队列 每次拿出来之后按照所选择的j进行分裂 #include<bits/stdc++.h> #define ...