Concurrent Collections
The java.util.concurrent package provides thread-safe collection implementations designed for concurrent access.
ConcurrentHashMap
Section titled “ConcurrentHashMap”A thread-safe implementation of Map that allows concurrent reads and a high level of concurrent writes.
Key Characteristics:
- Segments the map to allow multiple threads to modify different segments concurrently
- No locking for read operations in most cases
- Java 8+ implementation uses a more fine-grained approach with node-level locking
- Does not allow null keys or values
Performance:
- Read operations are typically not blocked
- Write operations only lock a small portion of the map
- Much better concurrent performance than Collections.synchronizedMap()
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
// Thread-safe operationsconcurrentMap.put("key1", 1);concurrentMap.put("key2", 2);
// Atomic operationsconcurrentMap.putIfAbsent("key1", 3); // Won't change existing valueInteger oldValue = concurrentMap.replace("key1", 4); // Returns previous valueboolean replaced = concurrentMap.replace("key1", 4, 5); // CAS operation
// Atomic computationsconcurrentMap.compute("key1", (k, v) -> v == null ? 1 : v + 1); // IncrementconcurrentMap.computeIfAbsent("key3", k -> k.length()); // Add if absentconcurrentMap.computeIfPresent("key1", (k, v) -> v * 2); // Double if present
// Bulk operationsconcurrentMap.forEach((k, v) -> System.out.println(k + ": " + v));ConcurrentHashMap vs HashMap with Synchronization
Section titled “ConcurrentHashMap vs HashMap with Synchronization”| Feature | ConcurrentHashMap | Collections.synchronizedMap(HashMap) |
|---|---|---|
| Locking granularity | Segment/node level | Whole map |
| Read performance | No locking for most reads | Locks entire map |
| Write performance | Locks only affected segments/nodes | Locks entire map |
| Iteration | Weakly consistent (reflects some but not all updates) | Fail-fast (throws ConcurrentModificationException) |
| Null values | Not allowed | Allowed |
| Memory overhead | Higher | Lower |
CopyOnWriteArrayList
Section titled “CopyOnWriteArrayList”A thread-safe variant of ArrayList where all mutative operations create a fresh copy of the underlying array.
Key Characteristics:
- Immutable snapshots for iteration (no ConcurrentModificationException)
- Best for read-heavy, write-rare scenarios
- Thread-safe without locking for reads
- High memory overhead for frequent modifications
List<String> copyOnWriteList = new CopyOnWriteArrayList<>();copyOnWriteList.add("item1");copyOnWriteList.add("item2");
// Safe iteration even if modified during iterationfor (String item : copyOnWriteList) { System.out.println(item); copyOnWriteList.add("newItem"); // Won't affect current iteration}
// Size reflects changes after iteration completesSystem.out.println(copyOnWriteList.size()); // 4 (item1, item2, newItem, newItem)CopyOnWriteArraySet
Section titled “CopyOnWriteArraySet”A Set implementation backed by CopyOnWriteArrayList.
Key Characteristics:
- Same copy-on-write semantics as CopyOnWriteArrayList
- Maintains insertion order like LinkedHashSet
- Uses equals() for element comparison
Set<String> copyOnWriteSet = new CopyOnWriteArraySet<>();copyOnWriteSet.add("item1");copyOnWriteSet.add("item2");copyOnWriteSet.add("item1"); // Duplicate, not added
// Safe iteration during modificationfor (String item : copyOnWriteSet) { System.out.println(item); copyOnWriteSet.add("newItem"); // Won't affect current iteration}ConcurrentSkipListMap and ConcurrentSkipListSet
Section titled “ConcurrentSkipListMap and ConcurrentSkipListSet”Thread-safe navigable map and set implementations based on skip lists.
Key Characteristics:
- Maintains elements in sorted order
- Expected O(log n) time for most operations
- Lock-free implementation for high concurrency
- Equivalent to concurrent TreeMap/TreeSet
// Concurrent sorted mapConcurrentNavigableMap<String, Integer> skipListMap = new ConcurrentSkipListMap<>();skipListMap.put("banana", 2);skipListMap.put("apple", 1);skipListMap.put("cherry", 3);
// Navigable operations are thread-safeMap.Entry<String, Integer> firstEntry = skipListMap.firstEntry(); // apple=1Map.Entry<String, Integer> higherEntry = skipListMap.higherEntry("banana"); // cherry=3ConcurrentNavigableMap<String, Integer> headMap = skipListMap.headMap("banana", true); // apple, banana
// Concurrent sorted setConcurrentNavigableSet<String> skipListSet = new ConcurrentSkipListSet<>();skipListSet.add("banana");skipListSet.add("apple");skipListSet.add("cherry");
// Navigable operationsString first = skipListSet.first(); // "apple"String ceiling = skipListSet.ceiling("b"); // "banana"BlockingQueue Implementations
Section titled “BlockingQueue Implementations”Thread-safe queue implementations that support blocking operations for producer-consumer patterns.
ArrayBlockingQueue
Section titled “ArrayBlockingQueue”A bounded blocking queue backed by an array.
// Fixed capacity of 10BlockingQueue<String> arrayQueue = new ArrayBlockingQueue<>(10);
// Producer threadnew Thread(() -> { try { for (int i = 0; i < 20; i++) { arrayQueue.put("Item " + i); // Blocks if queue is full Thread.sleep(100); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); }}).start();
// Consumer threadnew Thread(() -> { try { while (true) { String item = arrayQueue.take(); // Blocks if queue is empty System.out.println("Consumed: " + item); Thread.sleep(200); // Consuming slower than producing } } catch (InterruptedException e) { Thread.currentThread().interrupt(); }}).start();LinkedBlockingQueue
Section titled “LinkedBlockingQueue”An optionally bounded blocking queue based on linked nodes.
Key Characteristics:
- Can be bounded or unbounded (default constructor creates unbounded queue)
- Typically higher throughput than ArrayBlockingQueue but less predictable performance
// Unbounded queueBlockingQueue<Task> taskQueue = new LinkedBlockingQueue<>();
// Bounded queue with capacity 100BlockingQueue<Task> boundedTaskQueue = new LinkedBlockingQueue<>(100);PriorityBlockingQueue
Section titled “PriorityBlockingQueue”An unbounded blocking queue that orders elements according to their natural ordering or a comparator.
// Natural orderingBlockingQueue<Integer> priorityQueue = new PriorityBlockingQueue<>();priorityQueue.put(5);priorityQueue.put(2);priorityQueue.put(8);
// Elements come out in priority orderInteger first = priorityQueue.take(); // 2Integer second = priorityQueue.take(); // 5
// Custom comparator (process urgent tasks first)BlockingQueue<Task> taskQueue = new PriorityBlockingQueue<>(11, Comparator.comparing(Task::isUrgent).reversed() .thenComparing(Task::getCreationTime));DelayQueue
Section titled “DelayQueue”A blocking queue of delayed elements, which are not available until their delay has expired.
class DelayedTask implements Delayed { private final String name; private final long executeAt;
public DelayedTask(String name, long delayMs) { this.name = name; this.executeAt = System.currentTimeMillis() + delayMs; }
@Override public long getDelay(TimeUnit unit) { return unit.convert(executeAt - System.currentTimeMillis(), TimeUnit.MILLISECONDS); }
@Override public int compareTo(Delayed o) { return Long.compare(getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS)); }
@Override public String toString() { return name; }}
// Queue of delayed tasksDelayQueue<DelayedTask> delayQueue = new DelayQueue<>();delayQueue.put(new DelayedTask("Task 1", 5000)); // Execute after 5 secondsdelayQueue.put(new DelayedTask("Task 2", 1000)); // Execute after 1 seconddelayQueue.put(new DelayedTask("Task 3", 3000)); // Execute after 3 seconds
// Tasks will be available in order of expiration: Task 2, Task 3, Task 1while (!delayQueue.isEmpty()) { DelayedTask task = delayQueue.take(); // Blocks until a task's delay expires System.out.println("Executing: " + task);}ConcurrentLinkedQueue and ConcurrentLinkedDeque
Section titled “ConcurrentLinkedQueue and ConcurrentLinkedDeque”Unbounded thread-safe queue and deque implementations based on linked nodes.
Key Characteristics:
- Non-blocking algorithm for high throughput
- Weakly consistent iterators
- No blocking operations (unlike BlockingQueue)
// Thread-safe queueQueue<String> concurrentQueue = new ConcurrentLinkedQueue<>();concurrentQueue.offer("item1");concurrentQueue.offer("item2");String item = concurrentQueue.poll(); // Retrieves and removes the head
// Thread-safe dequeDeque<String> concurrentDeque = new ConcurrentLinkedDeque<>();concurrentDeque.offerFirst("first");concurrentDeque.offerLast("last");String first = concurrentDeque.pollFirst();String last = concurrentDeque.pollLast();Choosing the Right Concurrent Collection
Section titled “Choosing the Right Concurrent Collection”| Collection Type | Implementation | Use When |
|---|---|---|
| Map | ConcurrentHashMap | Need thread-safe map with high concurrency |
| SortedMap | ConcurrentSkipListMap | Need thread-safe sorted map |
| List | CopyOnWriteArrayList | Read-heavy, write-rare scenarios |
| Set | CopyOnWriteArraySet | Read-heavy, write-rare scenarios with unique elements |
| SortedSet | ConcurrentSkipListSet | Need thread-safe sorted set |
| Queue | ConcurrentLinkedQueue | Need high-throughput non-blocking queue |
| BlockingQueue | ArrayBlockingQueue | Need bounded blocking queue with predictable performance |
| BlockingQueue | LinkedBlockingQueue | Need high-throughput blocking queue |
| BlockingQueue | PriorityBlockingQueue | Need blocking queue with priority ordering |
| BlockingQueue | DelayQueue | Need time-based scheduling queue |
| Deque | ConcurrentLinkedDeque | Need high-throughput non-blocking double-ended queue |