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

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

传说这是阿里的一道面试题: 也传说发这道题出来的作者去了tmail。下面是关于题目的描述:

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

Java代码   收藏代码
  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。

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

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

转载于:https://my.oschina.net/u/176507/blog/137880

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/403293.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MAC OS X 10.9.X下用命令行开启SSD trim的方法汇总

From: http://www.jb51.net/os/MAC/238531.html 网上有很多MAC OS X开启trim的教程&#xff0c;但都是老系统的了&#xff0c;并不支持现在的10.9.X版本&#xff0c;经过一番研究&#xff0c;终于找到了开启的新方法&#xff0c;这里分享给大家网上搜的好多都是以前的老方法&a…

简单话题:LED呼吸灯和串口LED指示灯

最近在串口发送引脚上接LED指示发送或者接收状态&#xff0c;但是需求是希望连续发送或者接收字符过程中LED闪烁&#xff0c;而不是保持在一个常量或者常灭的状态。首先&#xff0c;把实际电路图和串口时序贴出来: 可以看出串口发送接收空闲态为高电平&#xff0c;只要进行数据…

Mac OS X 10.10.3对SSD开启Trim功能

From: http://bbs.pcbeta.com/viewthread-1515756-1-1.html 环境&#xff1a; SSD: Mac OS X 10.10.3 混合硬盘(普通机械硬盘8GB SSD): Win7 Mac OS X10.10.3 直接修改Clover配置文件config.plist&#xff0c;找到对应地方进行修改&#xff1a; <key>KernelAndKextPat…

关于Binder的点点滴滴(二)

http://www.linuxidc.com/Linux/2011-07/39271p2.htm4、Binder协议 Binder协议基本格式是&#xff08;命令数据&#xff09;&#xff0c;使用ioctl(fd, cmd, arg)函数实现交互。其中cmd传递命令&#xff0c;arg传递参数&#xff0c;不同的命令需要传递的参数不同。4、1 Binder所…

Unable to execute dex: Multiple dex files define 解决方法

程序编译正常&#xff0c;在用Eclipse调试执行时&#xff0c;报错 Unable to execute dex: Multiple dex files define&#xff0c;总结了一下出现问题的场景&#xff0c;并根据以下使用场景进行分类。 如果是jar包添加或者引用问题&#xff0c;那么可以尝试&#xff1a; 方法…

(四面体)CCPC网络赛 HDU5839 Special Tetrahedron

1 CCPC网络赛 HDU5839 Special Tetrahedron2 题意&#xff1a;n个点&#xff0c;选四个出来组成四面体&#xff0c;要符合四面体至少四条边相等&#xff0c;若四条边相等则剩下两条边不相邻&#xff0c;求个数3 思路&#xff1a;枚举四面体上一条线&#xff0c;再找到该线两个端…

mac下使用sshpass实现ssh记住密码

From: http://tinyhema.iteye.com/blog/2093795 由于有一些场景不能使用ssh私钥来实现免登&#xff0c;因此需要想其它办法解决一下这个问题。 安装sshpass 试图使用homebrew安装 Shell代码 $ brew install sshpass Error: No available formula for sshpass We wont …

ESXI忘记密码怎么办?

忘记ESX root用户的密码怎么办? 以单用户模式进入COS&#xff0c;然后修改root密码&#xff0c;既可搞定。第1步&#xff1a;打开/重启ESX主机图1第2步&#xff1a;到GRUB菜单处 (图2) &#xff0c;用键盘上下键&#xff0c;将光标放在“VMware ESX 4.0”上&#xff0c;按“a”…

TCPDUMP/LIBPCAP 1-由零开始

简介 TCPDUMP是强大的网络包分析器&#xff0c;可以在线或离线抓包&#xff0c;设置过滤条件等操作。 LIBPCAP是十分简洁易用的C/C网络流量抓包库&#xff1b;实际上TCPDUMP就是基于LIBPCAP实现的一个应用程序。为什么要学会工具和库的使用 工欲善其事必先利其器&#xff0c…

JavaSE第九天20160815

抽象与接口 抽象:abstracta) 修饰类&#xff1a;抽象类&#xff0c;抽象类不能实例化(不能new)&#xff0c;只能使用抽象类的子类。抽象类也有构造方法(子类会在自己构造方法的第一行调用父类的构造方法)。 b) 修饰方法&#xff1a;抽象方法。抽象方法只有方法…

mac下nginx搭配php-fpm解析php文件

From: http://ju.outofmemory.cn/entry/74778 1. 为单个项目添加nginx的php-fpm配置. 在server中添加php-fpm的配置. server{listen 80;server_name demo.local;index index.html index.htm index.php;root /path/to;location ~ \.php$ {fastcgi_pass 127.0.0.1:9000;fastc…

TCPDUMP/LIBPCAP 2-搭建环境

1. 实验平台   Linux 发行版本众多&#xff0c;考虑到大部分开发者的习惯&#xff0c;因此决定采用桌面版系统&#xff0c;具体为 CentOS-6.5-x86_64。系统内核版本为&#xff1a;2.6.32。尽管内核版本较低&#xff0c;但考虑到兼容性和稳定性&#xff0c;选择了此版本系统作…

使用jquery的blockui插件显示弹出层

使用jquery的blockui插件显示弹出层 Posted on 2011-04-14 16:34 孤独者 阅读(9975) 评论(0) 编辑 收藏 在做网站的开发过程中&#xff0c;可能需要使用弹出层&#xff0c;使用jquery的blockui插件可以很轻松的实现这个效果。blockui可以在你发送ajax请求的时候&#xff0c;显…

JS页面跳转大全

所谓的js页面跳转就是利用javesrcipt对打开的页面ULR进行跳转&#xff0c;如我们打开的是A页面&#xff0c;通过javsrcipt脚本就会跳转到B页面。目前很多垃圾站经常用js跳转将正常页面跳转到广告页面&#xff0c;当然也有一些网站为了追求吸引人的视觉效果&#xff0c;把一些栏…

CentOS SSH公钥登录问题

From: http://segmentfault.com/q/1010000000445726 内网&#xff0c;想做ssh root公钥登录&#xff0c;配置好之后还是提示输入密码&#xff0c;现象&#xff1a; 在服务器端使用其他端口开放sshd&#xff1a; $/usr/sbin/sshd -p 1234此时客户端可以无密码登录&#xff0c;但…

TCPDUMP/LIBPCAP 3-PCAP's MAN手册(1)

概要 #include <pcap/pcap.h> 说明 PCAP提供为抓包系统提供高级接口。网络上的所有数据包&#xff0c;即使是发往其他主机的数据包&#xff0c;都可以通过这种机制访问。它还支持将捕获的数据包保存到“savefile”&#xff0c;和从“savefile”中读取数据包。 打开捕…

提升用户体验,你不得不知道的事儿——三种提醒框的微技巧

大家都知道无论是android开发还是其他的开发&#xff0c;用户的体验都是很重要的事儿&#xff0c;下面就android开发中的三种提醒方式&#xff0c;Toast,SnackBar,Dialog做一些细节上的处理&#xff0c;或许能让你的产品更有用户亲和力。 1&#xff09;Toast Toast是一个轻量级…

分享:wkhtmltoimage开源工具的基本应用

wkhtmltoimage开源工具的基本应用 http://my.oschina.net/lidonghao/blog/90083

SVN:冲突解决 合并别人的修改

在项目中&#xff0c;基本不可避免多个人同时参与一个项目&#xff0c;因此就可能会出现多个人同时修改一个文件的情况&#xff0c;就不可避免的会出现冲突。svn已经很聪明了&#xff0c;如果你和别人对于同一个文件的修改之间不存在重叠&#xff08;比如你在文件最开始增加了一…

Error: Could not find or load main class

在 java 编程中如果遇到此错误&#xff0c;那么检查下 classpath。 必须确保把 .class 文件的路径添加到 classpath 中&#xff0c; 如果是在当前路径&#xff0c;那么就添加字符 . 到 classpath 中。注意不同系统环境下使用的路径分隔符是不同的&#xff0c;在 windows 下是分…