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 ArrayList
, HashSet
, 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 byCopyOnWriteArrayList
. - 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 (putIfAbsent
, compute
)
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 Case | Recommended Collection |
---|---|
Read-heavy List | CopyOnWriteArrayList |
Read-heavy Set | CopyOnWriteArraySet |
High-concurrency Map | ConcurrentHashMap |
Sorted Set | ConcurrentSkipListSet |
Sorted Map | ConcurrentSkipListMap |
5.2 Atomic Operations
- Use atomic methods (
putIfAbsent
,compute
,merge
) 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 (
ArrayList
,HashMap
) 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 useCollections.synchronizedCollection()
for custom thread-safe collections.
7. Summary
Collection | Thread-Safe? | Best For | Key Feature |
---|---|---|---|
CopyOnWriteArrayList | ✅ | Read-heavy lists | Copy-on-write |
CopyOnWriteArraySet | ✅ | Read-heavy sets | Copy-on-write |
ConcurrentHashMap | ✅ | High-concurrency maps | Lock striping |
ConcurrentSkipListSet | ✅ | Sorted sets | Skip list |
ConcurrentSkipListMap | ✅ | Sorted maps | Skip 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.