普通集合线程不安全因其未同步,多线程读写会引发ConcurrentModificationException、数据丢失或结构损坏;ConcurrentHashMap通过CAS加锁单桶、无锁读、协作扩容等机制避免锁竞争。
为什么普通集合在多线程下会出错
因为
、
、
这些类没做同步,多个线程同时读写时,可能触发
,或产生
数据丢失
、脏读、结构损坏(比如链表成环)。
典型现象:遍历
时另一个线程调用
,抛出
;或者
在扩容时多个线程同时操作,导致死循环(JDK 7)或数据覆盖(JDK 8+)。
不是“偶尔出错”,而是**未定义行为**——结果不可预测,且难以复现
和
虽线程安全,但用的是全表锁,吞吐量极低,不适合高并发场景
手动加
锁粒度难控制:锁太粗影响性能,锁太细容易漏掉临界区
是怎么避免锁竞争的
JDK 8 的
放弃了分段锁(
),改用「CAS + synchronized 锁单个桶(bin)」策略,写操作只锁定哈希表中实际发生修改的那个链表头或红黑树根节点。
读操作完全无锁,依赖
变量和
的有序读取保证可见性
扩容时支持多线程协作迁移,老表仍可读,新旧表并存过渡,不阻塞读写
不再是 O(1),而是对每个 bin 的计数求和,可能有短暂误差(最终一致)
不支持
和
之外的复合操作原子性,比如“检查不存在再 put”得用
哪些并发容器对应哪些使用场景
选错容器会导致功能缺失或性能浪费。关键看是否需要强一致性、是否允许重复、是否要阻塞等待:
Eclipse导入Android或其他的JAVA项目的正确方法 WORD版
本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
下载
立即学习
“
Java免费学习笔记(深入)
”;
需要线程安全的「栈」→ 用
(非阻塞)或
(阻塞,但得自己模拟栈逻辑);别用
或同步包装的
生产者-消费者模型 → 优先选
(有界、可重入锁)或
(无界、双锁分离);高吞吐低延迟场景考虑
(非 JDK)
需要线程安全的「计数器」→ 直接用
,别用
存一堆计数器,除非 key 动态可变
读远多于写的配置缓存 →
合适;但千万别在频繁修改的列表上用它,每次写都复制整个数组
常见误用和隐蔽陷阱
并发容器不是银弹,很多问题出在「以为线程安全,其实只是容器本身安全」:
的
返回的集合不是实时快照,遍历时若其他线程修改,不会抛异常,但可能漏数据或重复 —— 需要强一致性遍历请用
先拷贝
的
是 O(n),高并发下返回值可能瞬间过期,不能用于条件判断(如
)
的迭代器不支持
,调用会抛
;它的
也是写时复制,不是原地更新
所有并发容器都不保证「复合操作」原子性,比如
依然存在竞态,必须换成
或
最常被忽略的一点:并发容器解决的是「容器自身结构与元素引用的线程安全」,不解决元素对象内部状态的线程安全。如果往
里放可变对象,还得确保
自身是线程安全的,或访问时额外同步。
ArrayListHashMapHashSetConcurrentModificationExceptionArrayListadd()ConcurrentModificationExceptionHashMapVectorHashtablesynchronizedConcurrentHashMapConcurrentHashMapSegmentvolatileUnsafesize()containsKey()get()computeIfAbsent()ConcurrentLinkedDequeArrayBlockingQueueStackLinkedListArrayBlockingQueueLinkedBlockingQueueDisruptorAtomicIntegerConcurrentHashMapCopyOnWriteArrayListConcurrentHashMapkeySet()new ArrayList(map.keySet())ConcurrentLinkedQueuesize()if (q.size() > 0) q.poll()CopyOnWriteArrayListremove()UnsupportedOperationExceptionset()map.containsKey(k) ? map.get(k) : nullmap.getOrDefault(k, null)computeIfPresent()ConcurrentHashMapMyMutableObj