Java 集合框架:有些是线程安全的,有些不是?

Java Collection Framework: Some thread safe and some not?

我正在学习 Java 集合框架(不是并发集合框架),我开始知道有些集合实现是线程安全的,有些则不是。

在我阅读的大部分资料中,所有提到的 xyz 是线程安全的,而 abc 不是线程安全的。

但是,决定是否保持给定集合类型(例如,List、Set、Queue,甚至在 Map.. 中)线程安全的逻辑是什么?

我的问题是关于 "Traditional" 集合框架而不是并发集合框架。

任何有助于理解这一点的意见都会有很大帮助。

线程安全会带来开销(尽管在现代 VM 中,开销比设计收集框架时要低得多)。因此,除非特别需要,否则集合不是线程安全的,JDK1.1 集合除外——在设计它们时,哲学更像是 "let's leave as little room for error, at the cost of some performance"。

我们在 Java API 进化中有几个阶段。

JDK1.1

在 Java 的 1.1 版中,我们有数据结构 VectorHashtable。它们是完全同步的,提供了一定程度的线程安全。

JDK1.2

在 Java 的 1.2 版中,引入了集合框架。 None 的基本集合是线程安全的(它们不同步任何操作):ArrayListLinkedListHashMapTreeMapSet 实现。

但是你可以通过调用Collections.synchronizedMapCollections.synchronizedList等获取同步版本

JDK1.5

在 Java 的 1.5 版中,引入了 java.util.concurrent 框架。它们包含专门为多线程使用而构建的数据。这些提供了一定程度的线程安全。


请注意,即使使用同步集合,也可能引入数据竞争;它只意味着你不能破坏集合的内部结构(集合的所有不变量将被保留)

例如,如果您有一个分两步的过程,您首先要检查集合中是否包含某些元素,然后在第二步中插入该元素。如果您不为这两个步骤提供自己的同步,则如果两个线程同时执行此操作,则可以将元素添加两次。

原因很可能与性能有关。多线程之间的同步是一项昂贵的操作,尤其是对于大量元素。

正如其他人所说,并发集合具有运行时和潜在的内存开销,因此线程安全和不安全集合是分开的。

您可以在单线程库中找到的大多数数据结构都有多个线程安全的替代方案。一个值得注意的例外是 List,这可能是因为在应用程序中很少需要并发列表。

对于队列和堆栈之类的东西,您有大量的选择,因为生产者和一个或多个消费者同时拉动和推送队列是很常见的事情。要实现缓存,您可能依赖于地图,这就是为什么并发地图也得到很好的支持。

某些数据结构并未真正反映在线程安全 API 中的事实仅仅是因为它们通常不会在多线程上下文中有用。