基于HashMap+读写锁的包装类是否线程安全?volatile影响解析
这个HashMap包装类是否真的线程安全?
结论:这个LockMap类是完全线程安全的,包括你关心的修改可见性问题,都能通过读写锁的内存语义得到保障,不需要依赖HashMap底层数组的volatile修饰。
核心原因:读写锁的内存屏障机制
ReentrantReadWriteLock的写锁和读锁自带内存屏障,覆盖了可见性需求:
- 写锁的内存语义:当线程执行
put持有写锁时,lock()会禁止指令重排序,确保之前的本地修改不会被延迟;unlock()会强制将当前线程对map的所有修改(包括HashMap底层table的更新、扩容)刷入主内存,同时让其他线程的CPU缓存失效。 - 读锁的内存语义:线程执行
get获取读锁时,会触发内存屏障,强制从主内存读取数据,而不是使用本地缓存的旧值。而且读锁的获取会依赖写锁释放的内存屏障效果——写锁释放后的所有修改,对后续所有读锁持有者都是可见的。
为什么不需要HashMap底层数组是volatile
HashMap的table数组确实没有volatile修饰,但这没关系:
- 所有对
table的修改(put时的节点插入、扩容)都在写锁保护下执行,写锁解锁时的内存屏障已经把修改同步到了主内存; - 所有读取
table的操作(get时的数组寻址)都在读锁保护下执行,读锁的内存屏障确保读取到的是主内存的最新状态,不会读到缓存中的旧数据。
额外保障:操作的原子性
除了可见性,读写锁还保证了操作的原子性:
- 写锁是排他锁,同一时间只有一个线程能执行
put,避免了HashMap并发put时可能出现的链表环、数据丢失等经典问题; - 读锁是共享锁,多个线程可以同时读,但读期间不会有写操作干扰,避免读到半更新的无效状态。
内容的提问来源于stack exchange,提问作者Patanouk
相关产品推荐
相关产品推荐

