Java -- 每日一问:如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java -- 每日一问:如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全?相关的知识,希望对你有一定的参考价值。


Java

典型回答

Java 提供了不同层面的线程安全支持。在传统集合框架内部,除了 Hashtable 等同步容器,还提供了所谓的同步包装器(Synchronized Wrapper),我们可以调用 Collections 工具类提供的包装方法,来获取一个同步的包装容器(如 Collections.synchronizedMap),但是它们都是利用非常粗粒度的同步方式,在高并发情况下,性能比较低下。

另外,更加普遍的选择是利用并发包提供的线程安全容器类,它提供了:

  • 各种并发容器,比如 ConcurrentHashMap、CopyOnWriteArrayList。
  • 各种线程安全队列(Queue/Deque),如 ArrayBlockingQueue、SynchronousQueue。
  • 各种有序容器的线程安全版本等。

具体保证线程安全的方式,包括有从简单的 synchronize 方式,到基于更加精细化的,比如基于分离锁实现的 ConcurrentHashMap 等并发实现等。具体选择要看开发的场景需求,总体来说,并发包内提供的容器通用场景,远优于早期的简单同步实现。

高手回答

1.7
put加锁
通过分段加锁segment,一个hashmap里有若干个segment,每个segment里有若干个桶,桶里存放K-V形式的链表,put数据时通过key哈希得到该元素要添加到的segment,然后对segment进行加锁,然后在哈希,计算得到给元素要添加到的桶,然后遍历桶中的链表,替换或新增节点到桶中

size
分段计算两次,两次结果相同则返回,否则对所以段加锁重新计算

1.8
put CAS 加锁
1.8中不依赖与segment加锁,segment数量与桶数量一致;
首先判断容器是否为空,为空则进行初始化利用volatile的sizeCtl作为互斥手段,如果发现竞争性的初始化,就暂停在那里,等待条件恢复,否则利用CAS设置排他标志(U.compareAndSwapInt(this, SIZECTL, sc, -1));否则重试
对key hash计算得到该key存放的桶位置,判断该桶是否为空,为空则利用CAS设置新节点
否则使用synchronize加锁,遍历桶中数据,替换或新增加点到桶中
最后判断是否需要转为红黑树,转换之前判断是否需要扩容

size
利用LongAdd累加计算

你的朋友是不是也在准备面试呢?你可以把今天的题目分享给好友,或许你可以帮到他。


以上是关于Java -- 每日一问:如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全?的主要内容,如果未能解决你的问题,请参考以下文章

Java -- 每日一问:对比 VectorArrayListLinkedList 有何区别?

Java -- 每日一问:Java内存模型中的happen-before是什么?

如何保证集合是线程安全的?

如何保证集合是线程安全的?

Java -- 每日一问:如何写出安全的 Java 代码?

Java -- 每日一问:什么情况下Java程序会产生死锁?如何定位修复?