Concurrent Collections: Scalable Performance
Standard collections like ArrayList and HashMap are not thread-safe. Using them in concurrent environments leads to data corruption, infinite loops (in legacy HashMap resizing), and race conditions. While Collections.synchronizedMap exists, it uses a single global lock that bottlenecks performance. JUC concurrent collections offer superior scalability.
1. ConcurrentHashMap: The Performance King
1.1 JDK 7: Segment Locking
JDK 7 divided the map into 16 Segments, each acting as an independent ReentrantLock. This allowed 16 simultaneous writers. However, the granularity was still coarse, and the number of segments was fixed at construction.
1.2 JDK 8+: Bucket-Level Locking
JDK 8 abandoned segments for Bucket-Level locking:
- CAS for Empty Buckets: If a bucket is empty, a new node is inserted via CAS (Zero locking cost).
- Synchronized for Occupied Buckets: Only the head node of the bucket is locked using
synchronized. This allows every bucket to be accessed independently, dramatically increasing parallelism. - Parallel Resizing: When the map grows, multiple threads can participate in the migration by processing "strides" of the table simultaneously using
ForwardingNodes.
1.3 Why No "Null" Keys or Values?
Unlike HashMap, ConcurrentHashMap prohibits nulls to eliminate ambiguity. In a concurrent map, if get(key) returns null, you cannot distinguish between "Key doesn't exist" and "Value is null" using containsKey(), because another thread could have modified the entry between the two calls.
2. CopyOnWriteArrayList: Read-Heavy Optimization
CopyOnWriteArrayList uses a Write-at-Write strategy.
- Reads: Performed on a
volatilearray without any locks. - Writes: The internal array is copied entirely, modified, and then the reference is swapped.
Trade-offs:
- Pros: Extremely fast reads; Iterators never throw
ConcurrentModificationException. - Cons: Expensive writes (memory usage spikes); "Weakly consistent" (readers might see stale data for a split second).
3. BlockingQueue: The Backbone of Producer-Consumer
Blocking queues are the primary mechanism for decoupling producers and consumers.
| Implementation | Characteristics |
|---|---|
| ArrayBlockingQueue | Bounded, single lock for both ends. Low GC overhead. |
| LinkedBlockingQueue | Optional bounded, dual locks (Head/Tail separation). Higher throughput. |
| SynchronousQueue | Zero capacity. Direct hand-off between threads. |
| DelayQueue | Elements are only available after their expiration time. |
4. ConcurrentSkipListMap: The Ordered Choice
When you need a thread-safe version of TreeMap (Sorted Map), use ConcurrentSkipListMap. It uses a Skip List data structure (multiple levels of linked lists) to achieve $O(\log n)$ performance for all operations without the complexity of balancing a Red-Black tree in a concurrent environment.
Technical Selection Matrix
| Requirement | Recommended Collection |
|---|---|
| General Purpose Map | ConcurrentHashMap |
| Sorted Map | ConcurrentSkipListMap |
| Read-Heavy List | CopyOnWriteArrayList |
| High-Throughput Queue | LinkedBlockingQueue |
| Delayed Execution | DelayQueue |
| Direct Hand-off | SynchronousQueue |