如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全?
Java集合框架的典型容器类,它们绝大部分都不是线程安全的,仅有的线程安全实现,比如Vector、Stack,在性能方面也远不尽如人意。幸好Java语言提供了并发包(java.util.concurrent),为高度并发需求提供了更加全面的工具支持。
今天我要问你的问题是,如何保证容器是线程安全的?ConcurrentHashMap如何实现高效地线程安全?
典型回答
-
Java提供了不同层面的线程安全支持。在传统集合框架内部,除了Hashtable等同步容器,还提供了所谓的同步包装器(Synchronized Wrapper),我们可以调用Collections工具类提供的包装方法,来获取一个同步的包装容器(Collections.synchronizedMap),但是它们都是利用非常粗粒度的同步方式,在高并发情况下,性能比较低下
-
另外,更加普遍的选择是利用并发包提供的线程安全容器类,它提供了:
- 各种并发容器,比如ConcurrentHashMap、CopyOnWriteArrayList
- 各种线程安全队列(Queue/Deque),如ArrayBlockingQueue、SynchronousQueue
- 各种有序容器的线程安全版本等。
-
具体保证线程安全的方式,包括有从简单的synchronize方式,到基于更加精细化的,比如基于分离锁实现的ConcurrentHashMap等并发实现等。具体选择要看开发的场景需求,总体来说,并发包内提供的容器通用场景,远优于早期的简单同步实现
考点分析
- 谈到线程安全和并发,可以说是Java面试中必考的考点,我上面给出的回答是一个相对宽泛的总结,而且ConcurrentHashMap等并发容器实现也在不断演进,不能一概而论
- 如果要深入思考并回答这个问题及其扩展方面,至少需要:
- 理解基本的线程安全工具
- 理解传统集合框架并发编程中Map存在的问题,清楚简单同步方式的不足
- 梳理并发包内,尤其是ConcurrentHashMap采取了哪些方法来提高并发表现
- 最好能够掌握ConcurrentHashMap自身的演进,目前的很多分析资料还是基于其早期版本