为何ConcurrentHashMap需Segment数组?能否改为对Node加锁?
Let me break down your questions step by step—this is a super common point of confusion when navigating the older vs. newer implementations of ConcurrentHashMap.
First: Clarifying the Segment and Entry Array Relationship
You noticed public final Segment[] segments = new Segment[32] and a single protected transient Entry[] tab, but that tab belongs to the Segment class itself, not the top-level ConcurrentHashMap.
Each Segment in the segments array acts as an independent "mini hash table" with its own Entry[] tab (the array of linked lists storing key-value pairs). So in the default setup, there are actually 32 separate Entry arrays (one per Segment), not just one. When you perform a put/get operation, the key's hash first determines which Segment to target, then the hash is reused to index into that Segment's Entry array.
Why Use a Segment Array? The Core: Lock Striping
Segments exist to implement lock striping—a fine-grained locking strategy built for high concurrency. Here's why this was essential in Java 7:
- Before Java 8, synchronized locks had significant overhead, and CAS operations weren't as widely used for complex data structures. Using a single global lock (like Hashtable does) would mean only one thread could modify the map at a time, totally killing concurrent performance.
- Segment locks split the map into isolated partitions. When a thread modifies data in one Segment, it only locks that specific Segment—threads working on other Segments can run without waiting. The default 32 Segments let up to 32 threads write to different parts of the map simultaneously, a massive upgrade over a single lock.
Why Not Just Lock Each Node Instead?
Excellent question—and this is exactly what Java 8's ConcurrentHashMap does! But there were key tradeoffs that made Segment locking a better fit for Java 7:
- In Java 7, the overhead of acquiring/releasing a lock for every individual Node (especially with frequent small operations) was too high. Constant context switching between threads contending for dozens of tiny locks would have erased the benefits of fine-grained locking.
- Segments hit a sweet spot: they're coarse enough to keep lock overhead low, but fine enough to enable meaningful concurrency. Java 8 switched to Node-level locking (using synchronized on Node heads plus CAS for non-blocking ops) because JVM improvements made synchronized locks far lighter (via biased locking, lightweight locking, and lock elision).
To wrap up: Segments were Java 7's solution to building a high-concurrency hash map, using lock striping to balance performance and overhead. Java 8 refined this with more granular locking, but the core goal—maximizing concurrent access while keeping the map thread-safe—stays the same.
内容的提问来源于stack exchange,提问作者uideveloperui

