Segment Locking and Bin-Level Locking in Java
1. Introduction
In concurrent programming, managing access to shared data is crucial. Earlier, Java used segment-level locks in ConcurrentHashMap to balance safety and performance.
However, as multi-core processors grew in number and capacity, the need for even finer control led to the introduction of bin-level locking in Java 8.
Both strategies — segment locking and bin-level locking — aim to reduce contention and allow more threads to modify data structures simultaneously.
2. From Single Lock to Segment Locking
A synchronized map (like Collections.synchronizedMap()) uses one global lock, blocking all threads during any modification.
This approach is thread-safe but inefficient:
even if two threads modify completely different keys, they must wait for each other.
To solve this, Java 7’s ConcurrentHashMap introduced segment locking.
3. What Is Segment Locking?
Segment locking divides a map into multiple independent segments, each protected by its own lock.
Each segment manages a subset of keys (buckets) based on the hash code.
When a thread wants to modify a key-value pair:
- It finds which segment the key belongs to.
- Locks only that segment.
- Performs the operation safely.
- Releases the lock.
This allows multiple threads to modify different segments concurrently.
Conceptual Diagram
ConcurrentHashMap (Java 7)
+---------------------------------------------+
| Segment 1 | Segment 2 | Segment 3 | Segment 4 |
| Lock 1 | Lock 2 | Lock 3 | Lock 4 |
+---------------------------------------------+
↑ ↑ ↑ ↑
Thread A Thread B Thread C Thread D
Each thread can operate on a different segment without interference.
4. How Segment Locking Works (Simplified Example)
import java.util.concurrent.locks.ReentrantLock;
public class SegmentedMap {
private static final int SEGMENT_COUNT = 4;
private final Segment[] segments;
public SegmentedMap() {
segments = new Segment[SEGMENT_COUNT];
for (int i = 0; i < SEGMENT_COUNT; i++) {
segments[i] = new Segment<>();
}
}
private int segmentIndex(Object key) {
return Math.abs(key.hashCode() % SEGMENT_COUNT);
}
public void put(K key, V value) {
int index = segmentIndex(key);
segments[index].put(key, value);
}
public V get(K key) {
int index = segmentIndex(key);
return segments[index].get(key);
}
static class Segment {
private final ReentrantLock lock = new ReentrantLock();
private final java.util.HashMap map = new java.util.HashMap<>();
public void put(K key, V value) {
lock.lock();
try {
map.put(key, value);
} finally {
lock.unlock();
}
}
public V get(K key) {
lock.lock();
try {
return map.get(key);
} finally {
lock.unlock();
}
}
}
}
This demonstrates how each segment has its own lock, enabling concurrent operations on different segments.
5. Limitation of Segment Locking
While this approach increases concurrency, it’s still coarse-grained:
- If multiple keys hash into the same segment, threads must wait even if they modify different keys within that segment.
- A fixed number of segments (usually 16) limits scalability.
To overcome this, Java 8 introduced bin-level locking.
6. What Is Bin-Level Locking?
In Java 8’s redesigned ConcurrentHashMap, segment locks were removed and replaced by bin-level locking.
Instead of locking large portions (segments), locks are applied only to individual bins (buckets) — the linked lists or tree nodes that store entries with the same hash index.
Each bin can be independently locked, allowing:
- True fine-grained concurrency.
- Better CPU utilization on multi-core processors.
- Dynamic scalability with the number of keys.
7. How Bin-Level Locking Works Internally
Each bin is represented by a node (linked list or tree).
When a thread needs to update a particular bin, it locks only that bin’s first node.
Key mechanisms:
- CAS (Compare-And-Swap): Used for inserting a new bin if it doesn’t exist (lock-free insertion).
- Synchronized blocks: Used to lock a bin’s head node only during updates or modifications.
- No pre-defined segments: The number of bins grows dynamically based on table size.
Visualization
ConcurrentHashMap (Java 8+)
+----------------------------------------------------+
| Bin 0 | Bin 1 | Bin 2 | Bin 3 | ... | Bin N |
| Lock | Lock | Lock | Lock | | Lock |
+----------------------------------------------------+
↑ ↑ ↑
Thread A Thread B Thread C
Each thread locks only the bin it needs, not the entire segment or table.
8. Simplified Example of Bin-Level Locking Concept
Below is a simplified conceptual version demonstrating bin-level locking using per-bucket locks.
import java.util.concurrent.locks.ReentrantLock;
public class BinLockMap {
private static final int BUCKET_COUNT = 16;
private final Node[] table;
private final ReentrantLock[] locks;
@SuppressWarnings("unchecked")
public BinLockMap() {
table = new Node[BUCKET_COUNT];
locks = new ReentrantLock[BUCKET_COUNT];
for (int i = 0; i < BUCKET_COUNT; i++) {
locks[i] = new ReentrantLock();
}
}
private int bucketIndex(Object key) {
return Math.abs(key.hashCode() % BUCKET_COUNT);
}
public void put(K key, V value) {
int index = bucketIndex(key);
locks[index].lock(); // lock only this bucket
try {
Node current = table[index];
while (current != null) {
if (current.key.equals(key)) {
current.value = value; // update
return;
}
current = current.next;
}
table[index] = new Node<>(key, value, table[index]);
} finally {
locks[index].unlock();
}
}
public V get(K key) {
int index = bucketIndex(key);
locks[index].lock();
try {
Node current = table[index];
while (current != null) {
if (current.key.equals(key)) return current.value;
current = current.next;
}
return null;
} finally {
locks[index].unlock();
}
}
static class Node {
K key;
V value;
Node next;
Node(K key, V value, Node next) {
this.key = key;
this.value = value;
this.next = next;
}
}
}
Here:
- Each bin (bucket) has its own lock.
- Only that lock is acquired during modification.
- Other threads can access or modify other bins concurrently.
9. Advantages of Bin-Level Locking
| Benefit | Description |
|---|---|
| Higher concurrency | Each bin can be updated independently. |
| Dynamic scalability | Bins are created as needed; no fixed segment count. |
| Reduced contention | Locks are more localized and granular. |
| Improved CPU efficiency | Threads spend less time waiting for locks. |
10. When to Use Segment vs. Bin-Level Locking
| Approach | Used In | Locking Scope | Concurrency Level |
|---|---|---|---|
| Segment Locking | Java 7 ConcurrentHashMap | Segment (group of bins) | Moderate |
| Bin-Level Locking | Java 8+ ConcurrentHashMap | Individual bin | High |
11. Summary
| Feature | Segment Locking | Bin-Level Locking |
|---|---|---|
| Introduced In | Java 5 | Java 8 |
| Lock Type | ReentrantLock per segment | Synchronized per bin |
| Concurrency | Limited by segment count | Nearly per-entry concurrency |
| Mechanism | Fixed segments | Dynamic bins, CAS operations |
| Performance | Good | Excellent on multi-core systems |
12. Conclusion
Segment locking revolutionized thread-safe data structures by introducing partial concurrency.
Bin-level locking took it further, achieving fine-grained, near lock-free performance in modern Java.
Together, these concepts form the foundation of high-performance concurrent collections like ConcurrentHashMap.
Understanding both mechanisms helps developers appreciate the internal design evolution of Java’s concurrency framework.
