线程安全集合:多线程场景不踩坑的集合清单

线程安全集合:多线程场景不踩坑的集合清单

绝大多数日常开发的集合都是非线程安全的,多线程读写会直接出现数据错乱、空指针、数据覆盖问题,真正靠谱可用的线程安全集合分两类,老旧同步集合和JUC高性能并发集合。写多线程代码时,你真的分得清该选哪一款吗?

很多人初学都会踩一个低级坑,觉得Vector、Hashtable既然是线程安全的,那多线程场景直接用就行。我早年做批量数据导入功能时,就无脑用了Hashtable存储临时统计数据,上线后压测直接翻车。10个线程同时写入数据,接口响应直接从几十毫秒飙升到800多毫秒,CPU占用率直接拉满。后来才懂,这类老旧集合的安全是粗暴的全局锁,所有线程抢同一把锁,并发效率极低,现在基本不推荐在新项目里使用。

老旧同步集合:能用但不推荐

这类集合诞生于JDK早期,通过给所有方法加synchronized全局同步锁实现线程安全,优点是适配老旧代码、绝对稳妥,缺点是并发性能极差,读写都会互相阻塞。

Vector是ArrayList的线程安全版本,底层也是数组结构,增删改查方法全部加了同步锁。它和ArrayList用法几乎一致,但多线程读写时没有并发优势,单线程下性能也不如ArrayList。现在除了维护老项目,几乎没人主动使用。

Hashtable是最早期的线程安全哈希表,对应现在的HashMap。它不仅锁粒度极大,不允许key和value为null,功能受限、性能拉胯。相比之下,后续的并发集合全方位碾压它,完全没必要复用。

还有Collections工具类的同步方法,比如synchronizedList、synchronizedMap。它的原理是给普通集合套一层同步包装,临时解决线程安全问题。但它的迭代操作依然非线程安全,遍历的时候必须手动加锁,很容易遗漏出错。

JUC高性能并发集合:现代开发首选

JDK1.5推出的JUC包,彻底改写了并发集合的格局。摒弃了全局锁的笨重方案,用CAS乐观锁、分段锁、读写分离等轻量化机制,兼顾安全和性能,是现在多线程开发的标配。

CopyOnWriteArrayList是ArrayList的最佳并发替代。它的核心逻辑很简单,写数据时复制一份新数组修改,改完再替换原数组,读数据直接无锁访问。读极快。写稍慢。适合读多写少的场景,比如配置缓存、白名单列表。

它有个致命短板,不适合大批量写入。之前做日志实时存储,误用这个集合高频新增数据,频繁的数组复制直接导致内存抖动,频繁触发GC,页面卡顿明显。

ConcurrentHashMap是高并发键值存储的绝对主力。JDK8之后优化成CAS+局部锁,只锁住当前操作的哈希桶,不会影响其他数据的读写,并发吞吐量远超Hashtable。日常接口缓存、统计计数、参数映射,优先选它。

有序并发场景,就用ConcurrentSkipListMap、ConcurrentSkipListSet。它们基于跳表实现,天然有序,对比老旧的TreeMap、TreeSet,并发性能提升巨大,适合需要排序的并发数据存储。

常用并发队列:多线程流转必备

多线程任务排队、消息流转,离不开线程安全队列,核心就两款,很好区分。

  • ConcurrentLinkedQueue:无锁非阻塞队列,纯CAS实现,性能拉满,适合高并发无界任务流转,不阻塞线程。
  • ArrayBlockingQueue:有界阻塞队列,基于数组实现,容量固定,队列满就阻塞写入,队列空就阻塞读取,适合限流、生产者消费者场景。

选对场景很关键。

读多写少,用CopyOnWriteArrayList。

高频读写键值对,用ConcurrentHashMap。

任务排队限流,用阻塞队列。

老旧同步集合,一律摒弃。

后续写多线程代码,直接按这个场景匹配规则选型,不用再盲目试错。

了解更多百科知识请访问 百科