【搜狐技术面试】有关ConcurrentHashMap死锁的问题

深圳小兵 发布于 2016/05/24 12:20
阅读 1K+
收藏 2

昨天接到搜狐技术面试的电话,其中有问道这样一个问题:

ConcurrentHashMap会不会死锁?

当时想的是,ConcurrentHashMap的读是没有加锁的,写是有锁的,应该不会死锁。

然后被告知,是会发生死锁的。


于是回来之后,就开始看源码,百度,Google相关的问题。

找到最贴近的问答:http://stackoverflow.com/questions/3292577/is-it-possible-for-concurrenthashmap-to-deadlock

但是这个问题是10年提出来的,JDK用的是1.5的,后面提问者将JDK升级到1.6之后,好像就可以了。

另外一个问题 http://rainshow.iteye.com/blog/245769 也是用的JDK 1.5,并未找到原因。

另外相关的答案,大意都是说ConcurrentHashMap多段加锁要保持顺序,否则会发生死锁。但这个是JDK内部的实现,我们在使用的时候,并不需要做额外的操作。

关于ConcurrentHashMap死锁的问题,大概只能搜到这些相关的结果。

OK,我的问题如下:

JDK 1.5的ConcurrentHashMap是否会发生死锁?如果会,在什么样的情况下会?1.6之后是否修复了该问题?

跪求大神指点解惑,感激不尽......


PS1:我在JDK8的ConcurrentHashMap的size方法中看到如下代码:

    final long sumCount() {
        CounterCell[] as = counterCells; CounterCell a;
        long sum = baseCount;
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    sum += a.value;
            }
        }
        return sum;

    }

   加菜: 为何要用a,而不是直接使用as[i] ?

PS2:技术面试官知识面很广,而且对部分技术研究很深。重要的,是她人很nice,亲和力很强。难道搜狐的妹子都如此优秀,压力山大呀。。


PS3:难道你们只看到了妹子没有看到问题嚒?问题已高亮......


TO @令飞 了解嚒?

TO@红薯  大家都不严肃答题,肿么办?


加载中
1
Li_Peng
Li_Peng
不同版本jdk里面的ConcurrentHashMap实现上确实有变化,对方没有考虑jdk版本直接提问,其实也不严谨,你也没必要纠结这个问题。
Li_Peng
Li_Peng
回复 @深圳小兵 : 看Segment中的get方法,如果你使用的jdk版本和搜狐那个人的一样,可以看到此方法中有return readValueUnderLock(e)这么一句。
深圳小兵
深圳小兵
回复 @Li_Peng : 可否指明“某些”情况,是什么样的情况?
Li_Peng
Li_Peng
回复 @深圳小兵 : ConcurrentHashMap的putAll是对put的循环,put是要上锁的,而get在某些情况下调用readValueUnderLock,这个也是要上锁的,两者竞争可能出现死锁。
深圳小兵
深圳小兵
不严谨这点是对的,但是死锁的问题,别人遇到过,很有可能存在。我是想知道这个死锁的原因。
1
温安适
温安适

这个是底层操作系统不同造成的 ,只发生在

Solaris 10 (does not happen with Linux)


详情查看,http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6822370

大概因为,线程在等待一个没有线程持有的锁时被挂起(Solaris 10中)。

另外我认为面试你的人的意思是使用ConcurrentHashMap后还需不需要其他的同步业务策略,不恰当的使用同步工具也会造成死锁,但是因为没有使用同步策略造成的。

JDK6以上已经修复

1
永远在一起

我想我可以回答第二个为什么使用a,而不是使用as[i]的问题:

多线程中,如果要在同一个方法里面,两次使用成员对象、方法返回值,就必须在这种方案或者加锁两种方案中选择一个:

public class A {
  Object o = a;
  public void method1() {
     o = new xxx();
  }
  public void method2() {
     o == a; // 第一次使用o,这是true
     // 如果调用method2的线程在此处停止,系统转为使用method1的线程
     o == a; // 此时就是false。
  } 
  public void method3 () {
     Object t = o;
     t == a; // 第一次使用o
     // 即使调用method3的线程在此处停止,系统转换为使用那个method1的线程
     t == a; // 此时还是true,因为你改变的是成员变量o。
  }
}

通常而言,method2的行为是不希望的,使用临时变量保存就可以简单避免问题,如果不使用临时变量,就要对所有赋值给o的地方和读取两次o的方法进行加锁。与临时变量保存方法相比,孰优孰劣一目了然。

sumCount方法中的as和a的作用都是如此,as要用两次,a(即as[i])也要用两次,使用临时变量,简单快捷高效。

PS:有人可能会问,赋值操作并不是原子操作,在赋值处是否需要加锁,答案是不需要的。



深圳小兵
深圳小兵
THX,非常好。
0
采蘑菇的大叔
采蘑菇的大叔
没人回答问题~~~
深圳小兵
深圳小兵
是啊,一群无聊的人.....
-1
恶人
恶人
妹子心细啊
-1
菜菜打怪兽
菜菜打怪兽

什么,是妹子!!


-1
南湖船老大
南湖船老大
什么,是妹子!!
深圳小兵
深圳小兵
回复 @南湖船老大 : 对,我只是不明白是在那个环节导致了死锁,不知您是否知晓?
南湖船老大
南湖船老大
回复 @深圳小兵 : 只要使用了互斥锁,就不可避免会造成线程阻塞,并有可能产生死锁。
深圳小兵
深圳小兵
是我的问题太简单了,大神不屑回答?
-1
王涛
王涛
什么,是妹子!!
淘淘我的小宝宝
淘淘我的小宝宝
然而是人妇。
-1
不日小鸡
什么?是妹子还很nice!!
-1
Gillian_Male
Gillian_Male
什么?是妹子!!
返回顶部
顶部