Java’s Synchronized Collections

Rmag Breaking News

Java’s Collection framework is one of the foundational components of Java platform. Collections in Java represent a group of objects and the framework provides several interfaces and implementations to work with these collections. With such a rich framework at disposal, developers can focus on what matters the most!

With the advent of multi-threaded programming, the need for thread-safe collections grew. It is when Synchronized Collections were added to Java’s Collection framework. Let us understand what makes Synchronized Collections thread-safe and what are some things we should keep in-mind while using them.

How does Synchronized Collections ensure thread-safety?

Synchronized collections achieves thread-safety by enforcing synchronization on each of its publicly available method. In addition to that, it ensures that its internal state is never published. Thus, the only way to modify a collection is via its public synchronized methods!

Think of synchronized collections as plain unsynchronised collections plus state encapsulation and synchronized public methods.

Since every public method of a synchronized collection is guarded by the same (intrinsic) lock, no two threads can modify/read the collection at once. This ensures that the collection always maintain its variants and hence become thread-safe.

How to create Synchronized Collections?

The Collections class expose several static methods for creating synchronized collections. These static method are named in the following format — synchronizedXxx. Here is a list of these methods:

synchronizedCollection(Collection c)

synchronizedList(List list)

synchronizedMap(Map m)

synchronizedNavigableMap(NavigableMap m)

synchronizedNavigableSet(NavigableSet s)

synchronizedSet(Set s)

synchronizedSortedMap(SortedMap m)

synchronizedSortedSet(SortedSet s)

This is how you will create a synchronized list using the Collections class!

List<Integer> synchronizedIntegerList = Collections.synchronizedList(new ArrayList<>());

Pitfall of Compound Actions on Synchronized Collections

While methods exposed from synchronized collections are thread-safe, compound actions on client-side still require proper locking. Consider the following method —

public void putIfAbsent(List<Integer> synchronizedList, Integer elem) {
if(!synchronizedList.contains()) {
synchronizedList.add(elem);
}
}

This innocent looking method is actually thread-unsafe! Even with thread-safe contains and add methods, the putIfAbsent method does not achieve what it intended to. Between the ‘check’ and ‘act’ steps of our method, another thread can add the elem to the list. Guarding such compound actions is the responsibility of the client.

Synchronized collections allow client-side locking to ensure that such compound actions are atomic with respect to other operations. Each public method of the synchronized collection is guarded by its own intrinsic lock. It is thus possible for client to create atomic operation by acquiring the same lock.

Here is how we will fix putIfAbsent using client-side locking.

public void putIfAbsent(List<Integer> synchronizedList, Integer elem) {
synchronized(synchronizedList) {
if(!synchronizedList.contains()) {
synchronizedList.add(elem);
}
}
}

Pitfalls of Iterating on Synchronized Collections

Some extra care is needed to be taken when iterating over a synchronized collection. When iterating over a synchronized collection using iterators, any concurrent modifications to the collection will cause the iterator to fail-fast. On detecting any concurrent modification, the iterator will throw a ConcurrentModificationException.

final Set<String> syncStringSet = Collections.synchronizedSet(new HashSet<>());

// Might throw ConcurrentModificationException
for(String s: syncStringSet) {
doSomething(s);
}

To avoid getting a ConcurrentModificationException, we can hold a lock on the synchronized collection for the entire duration of iteration. Clearly this is not the most performant approach since the iteration can take a long time and it will block other executing threads from access to the collection!

Synchronized classes provides an easy mechanism to make your collection classes thread-safe. A thorough understanding of these would help you avoid common pitfalls associated with it.

With this we reach the end of this blog. I hope you enjoyed reading this piece.

If you like reading such articles, here are some other interesting reads from this series —

Object Sharing in Multi-Threaded environment
Visibility Across Threads
Thread Safety from an Object Oriented Perspective
A Nice Race Condition
Producer-Consumer pattern using Java’s Blocking Queue

Leave a Reply

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