3 Synchronization idioms

July 27, 2016

Signal

Intent

Change the behavior of a thread based on an event in another thread.

Motivation

In GUI Frameworks like swing the event thread handles all user events. Some events do not depend on the current state of other threads. An example is the cancel event for a long running operation. To notify worker threads an event handler uses the signal idiom.

Structure

volatile

The Signal idiom consists of a volatile field and at least on thread reading and one thread writing this field. The field must never be read and written in the same thread.

Sample Code

public class WorkerThread extends Thread {
public volatile boolean  canceled;
public void run()
{
while( ! canceled )
{
// do some work
}
}
}

Snapshot

Intent

See a consistent state of an object.

Motivation

A thread needs to make a long running computation with data which is potentially changed by other threads in the middle of the computation. An example is the deployment descriptor of a web application in a web server. Through automatic reloads the deployment descriptor might change in the middle of the processing of a web request. To see a consistent state the web request processing thread uses a snapshot of the deployment descriptor.

Structure

snapshot

The Snapshot idiom consists of a class holding the current snapshot. A thread reading values gets the current snapshot and reads values from this snapshot. A thread writing values clones the current snapshot, changes this copy and set the clone as new current snapshot.

Sample Code

public class CopyOnWriteArrayList {
	  final transient ReentrantLock lock = new ReentrantLock();
	  // usa a volatile field for the snapshot reference
	  private transient volatile Object[] array;
	  final Object[] getArray() {
	        return array;
	   }
	 final void setArray(Object[] a) {
	        array = a;
	   }
	    public boolean add(E e) {
	        final ReentrantLock lock = this.lock;
	        lock.lock();
	        try {
	            Object[] elements = getArray();
	            int len = elements.length;
	            // clone the object
	            Object[] newElements = Arrays.copyOf(elements, len + 1);
	            // change the local copy
	            newElements[len] = e;
	            // set the copy as new snapshot
	            setArray(newElements);
	            return true;
	        } finally {
	            lock.unlock();
	        }
	    }
	    public void forEach(Consumer action) {
	        if (action == null) throw new NullPointerException();
	        // work with the current snapshot
	        Object[] elements = getArray();
	        int len = elements.length;
	        for (int i = 0; i < len; ++i) {
	            @SuppressWarnings("unchecked")
	            // all read operations are done on this snapshot 
	            E e = (E) elements[i];
	            action.accept(e);
	        }
	    }
}
Taken from java.util.concurrent.CopyOnWriteArrayList. Comments are mine.

Put if absent

Intent

Get an object out of a map in a multithreaded environment. Create it if it does not exist.

Motivation

You have a map of objects which is accessed from many threads. Each thread behaves the same, checking if a value exists for a key, if not creating the value. You have many concurrent reads. A read should not be blocked by a write to a different key. For example in a web application the language specific formats are stored in a map, using the language as key. Each worker thread checks if the language specific formats are available in the map, if not the thread creates a new one.

Structure

putIfAbsent

A thread tries to get a value for a key calling get on a concurrent map. If the “get” method returns null it creates the missing value and calls putIfAbsent.

Sample Code

public Set  getLanguageTagSet(String category) {
    // get the value
    Set tagset = langtagSets.get(category);
    // if value is null create one
       if (tagset == null) {
            tagset = createLanguageTagSet(category);
            // call putIfAbsent
            Set ts = langtagSets.putIfAbsent(category, tagset);
            // if putIfAbsent returns a value a other thread has created a new value in between 
            if (ts != null) {
                tagset = ts;
            }
     }
     return tagset;
    }
Taken from sun.util.locale.provider.JRELocaleProviderAdapter. Comments are mine.

Conclusion

Each synchronization idiom can only be used for a specific access pattern. Using it outside this access pattern will lead to race conditions in your application. Therefore always use a tool like vmlens to detect race conditions during development and testing.

testing multi-threaded applications on the JVM made easy

LEARN MORE

© 2020 vmlens Legal Notice Privacy Policy