CODE WITH SIBIN

Solving Real Problems with Real Code


Java Thread-Safe Collections: Complete Guide to Concurrent List, Set, and Map

Java provides a robust set of concurrent collections in the java.util.concurrent package to handle multi-threaded operations efficiently. This guide covers Concurrent Lists, Sets, Maps, and threading concepts in depth.

1. Why Use Concurrent Collections?

In multi-threaded environments, traditional collections like ArrayListHashSet, and HashMap are not thread-safe, leading to:

  • Race conditions
  • Data corruption
  • ConcurrentModificationException

Concurrent collections solve these issues by providing thread-safe alternatives with optimized performance.

2. Concurrent Lists

2.1 CopyOnWriteArrayList

  • Thread-safe variant of ArrayList.
  • Every modification (add, remove) creates a new copy of the underlying array.
  • Best for read-heavy scenarios (few writes, many reads).

Example:

import java.util.concurrent.CopyOnWriteArrayList;

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("Java");
list.add("Python");

// Thread-safe iteration (no ConcurrentModificationException)
for (String item : list) {
    System.out.println(item);
}

Pros:

✔ No locks during reads
✔ Safe for iteration

Cons:

✖ High memory overhead (copies array on modification)
✖ Slow for frequent writes

3. Concurrent Sets

3.1 CopyOnWriteArraySet

  • Thread-safe Set backed by CopyOnWriteArrayList.
  • Best for small, read-heavy sets.

Example:

import java.util.concurrent.CopyOnWriteArraySet;

CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();
set.add("Java");
set.add("Python");

// Thread-safe iteration
for (String item : set) {
    System.out.println(item);
}

Pros & Cons:

Same as CopyOnWriteArrayList.

3.2 ConcurrentSkipListSet

  • Thread-safe NavigableSet implementation.
  • Uses skip list for log(n) time complexity.
  • Maintains elements in sorted order.

Example:

import java.util.concurrent.ConcurrentSkipListSet;

ConcurrentSkipListSet<Integer> skipListSet = new ConcurrentSkipListSet<>();
skipListSet.add(5);
skipListSet.add(2);

System.out.println(skipListSet); // [2, 5]

Pros:

✔ Sorted elements
✔ Efficient for concurrent access

Cons:

✖ Higher memory usage than HashSet

4. Concurrent Maps

4.1 ConcurrentHashMap

  • Most widely used concurrent map.
  • Lock striping: Divides the map into segments, allowing multiple threads to write simultaneously.
  • Does not lock the entire map (unlike Collections.synchronizedMap).

Example:

import java.util.concurrent.ConcurrentHashMap;

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("Java", 1);
map.put("Python", 2);

// Thread-safe operations
map.computeIfAbsent("C++", k -> 3);

Pros:

✔ High concurrency (multiple threads can read/write)
✔ No full lock on the map
✔ Atomic operations (putIfAbsentcompute)

Cons:

✖ No lock-free iteration (but safe)

4.2 ConcurrentSkipListMap

  • Thread-safe NavigableMap (sorted keys).
  • Uses skip list for log(n) operations.

Example:

import java.util.concurrent.ConcurrentSkipListMap;

ConcurrentSkipListMap<String, Integer> skipListMap = new ConcurrentSkipListMap<>();
skipListMap.put("Java", 1);
skipListMap.put("Python", 2);

System.out.println(skipListMap); // {Java=1, Python=2}

Pros:

✔ Sorted keys
✔ Concurrent access

Cons:

✖ Higher memory usage than ConcurrentHashMap

5. Threading Best Practices with Concurrent Collections

5.1 Choosing the Right Collection

Use CaseRecommended Collection
Read-heavy ListCopyOnWriteArrayList
Read-heavy SetCopyOnWriteArraySet
High-concurrency MapConcurrentHashMap
Sorted SetConcurrentSkipListSet
Sorted MapConcurrentSkipListMap

5.2 Atomic Operations

  • Use atomic methods (putIfAbsentcomputemerge) to avoid race conditions.
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

// Only put if key is absent
map.putIfAbsent("Java", 1);

// Atomic update
map.compute("Java", (k, v) -> v + 1);

5.3 Avoiding ConcurrentModificationException

  • Traditional collections (ArrayListHashMap) throw this during concurrent modification.
  • Concurrent collections allow safe iteration while modifying.

5.4 Performance Considerations

  • ConcurrentHashMap > Collections.synchronizedMap (better scalability).
  • CopyOnWriteArrayList is slow for frequent writes.

6. Advanced Topics

6.1 Weakly Consistent Iterators

  • Concurrent collections provide weakly consistent iterators:
    • Reflect the state of the collection at some point in time.
    • May or may not show recent modifications.

6.2 Parallel Streams with Concurrent Collections

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("Java", 1);
map.put("Python", 2);

// Parallel processing
map.forEach(2, (k, v) -> System.out.println(k + "=" + v));

6.3 Custom Concurrent Collections

  • Extend ConcurrentHashMap or use Collections.synchronizedCollection() for custom thread-safe collections.

7. Summary

CollectionThread-Safe?Best ForKey Feature
CopyOnWriteArrayListRead-heavy listsCopy-on-write
CopyOnWriteArraySetRead-heavy setsCopy-on-write
ConcurrentHashMapHigh-concurrency mapsLock striping
ConcurrentSkipListSetSorted setsSkip list
ConcurrentSkipListMapSorted mapsSkip list

8. Conclusion

  • Use ConcurrentHashMap for high-performance concurrent maps.
  • Prefer CopyOnWriteArrayList for read-heavy lists.
  • For sorted data, use ConcurrentSkipListSet/Map.
  • Avoid synchronized collections (they have full locks).

By leveraging these concurrent collections, you can build scalable, thread-safe applications efficiently.

Leave a Reply

Your email address will not be published. Required fields are marked *