Home首页 / Lesson 7: Threads第 7 课:线程
Lesson 7 · Concurrency第 7 课 · 并发
Why is HashMap not thread-safe?为什么 HashMap 是线程不安全的?
Two threads write at the same time, one write is lost. In JDK 7 it could even bend into a circle.两个线程同时写,就会丢掉一次写入;在 JDK 7 里甚至能把链表拧成一个圈。
01The idea先有一个画面
put is not one instruction. It is a series of steps: pick a bucket, read the head, write the new node, increase size. Another thread can jump in between any two steps.put 不是一条指令,而是一串步骤:选桶、读头节点、写新节点、size++。另一个线程可以在任意两步之间插进来。
02Three ways it breaks三种坏法
- Lost update — two
puts increase size, one increase is overwritten. The map says 1 item, there are 2.丢更新——两次 put 各涨一次 size,被覆盖掉一次:表里说有 1 个,实际有 2 个。 - Data lost — two threads add to the same empty bucket; one node overwrites the other.丢数据——两个线程同时往同一个空桶放,一个节点把另一个覆盖掉。
- Broken list / infinite loop — JDK 7 head-insert during resize could make a circle, then
getspins forever.链坏掉 / 死循环——JDK 7 扩容时头插法可能把链拧成圈,之后get永远转不出来。
Java 8 changed resize to keep the order (no circle), but data loss and wrong size still happen. “No circle” is not “thread-safe”.Java 8 把扩容改成保序(不会再成环),但丢数据和 size 不准依然存在。“不成环”不等于“线程安全”。
03How to fix it怎么解决
| Option方案 | Good for适合 | Watch out注意 |
|---|---|---|
| ConcurrentHashMap | web apps, shared cachesWeb 应用、共享缓存 | null keys not allowed不允许 null key |
| Collections.synchronizedMap | rare, small maps很少用、很小的表 | one lock for everything整表一把锁,慢 |
| ThreadLocal | per-user / per-request data每用户、每请求的数据 | memory leak in thread pools线程池里会内存泄漏 |
| Local variable局部变量 | inside one method方法内部 | nothing没有 |
04One sentence for the interview面试用一句话
Say it like this可以这样说HashMap is not thread-safe because put is a multi-step write: two threads can overwrite each other's size, lose a node, or in JDK 7 even build a circle and loop forever. In a web application I use ConcurrentHashMap.HashMap 线程不安全,因为 put 是多步写入:两个线程会互相覆盖 size、丢节点,JDK 7 里甚至会成环死循环。Web 应用里我用 ConcurrentHashMap。
→New words新词
Click ▶ hear it for real people on YouGlish; 🔊 for the computer voice.点 ▶ hear it 听真人发音(YouGlish);点 🔊 听电脑朗读。
✓Check yourself自测
- Why can size be wrong after two puts?为什么两次 put 之后 size 会不准?
- What was special about JDK 7 resize?JDK 7 的扩容有什么特别之处?
- What would you use in a Spring service, and why?Spring 服务里你会用什么?为什么?
Answer out loud first. Only then open the answers below.先自己出声答,再看下面的答案。