5 Ways to thread safe update a field in java

June 25, 2016

Here are the 5 possibilities to update a field in java in a thread safe way. But before we start, what do you have to make look at? If you access a field from many threads, you must make sure that:

    1. changes are made visible to all threads, and
    2. the value is not changed during the update by the other thread, and
    3. reading threads do not see the inconsistent intermediate state.
You can achieve this by one of the following 5 ways:

1) volatile Field

When to use?

You can use a volatile field when you have only one thread updating and many threads reading a single-valued field. Or use it, when the writing threads do not read the field. You can use it only for single valued fields like boolean or int. If you want to update object graphs or collections, use copy on write, as described below, instead.

Example

The following example code shows a worker thread, which stops processing based on a volatile field. This makes it possible, that other threads, like an event dispatching thread, stop the worker thread.
public class WorkerThread extends Thread {
	private volatile boolean canceled = false;
	public void cancelThread() {
		this.canceled = true;
	}
	@Override
	public void run() {
		while( ! canceled )
		{
			// Do Some Work		
		}		
	}
}

How does it work?

Declaring the field volatile makes changes made by one thread visible to all other threads. As a writing thread do not read the value, point b “the value is not changed during the update by the other thread” is fulfilled. Since the field is a single value point c “reading threads do not see inconsistent intermediate state” is also fulfilled.

How to test?

By using vmlens, an eclipse plugin to test multi-threaded software and to detect java race conditions, we can find fields, which should be declared volatile. After declaring the field volatile, we can check in the “order of event” view of vmlens, that the field is correctly read and written:

Order of Events in vmlens

2) copy on write

When to use?

Use copy on write, if you want to update a graph of objects or a collection and the threads mostly read and only rarely update.

Example

The following shows the add and get Method from java.util.concurrent.CopyOnWriteArrayList
private transient volatile Object[] array;
final Object[] getArray() {
  return array;
}
public boolean add(E e) {
  final ReentrantLock lock = this.lock;
  lock.lock();
  try {
  Object[] elements = getArray();
  int len = elements.length;
  Object[] newElements = Arrays.copyOf(elements, len + 1);
  newElements[len] = e;
  setArray(newElements);
  return true;
  } finally {
  lock.unlock();
  }
}
public E get(int index) {
  return get(getArray(), index);
}

How does it work?

Again declaring the field volatile makes changes made by one thread visible to the other threads. By using a lock around the updating method, we make sure that the value is not changed during the updating process. Since we copy the data before changing it, reading threads do not see the inconsistent intermediate state.

How to test?

We can test this by using a multithreaded test and adding a wait point inside vmlens at the read of the field.

Wait Point in vmlens

3) lock based atomic update

When to use?

Use locks, when updates and reads happen equally often. Use it until the lock becomes a bottleneck and you need the more performant solution compareAndSet as described below.

Example

The following example shows a lock based counter:
public class LockBasedCounter {
	private int i = 0;
	public synchronized void addOne()
	{
		i++;
	}
	public synchronized int get()
	{
		return i;
	}
}

How does it work?

The synchronize statements make sure that the changes made by one thread are seen by the other threads. Since only one thread can execute the methods protected by the lock at a given time, the value can not be changed during the update by another thread and the other threads can not see an intermediate inconsistent state.

How to test?

We can test this by using a multi-threaded test and adding a wait point at the updating method.

Wait Point in vmlens

4) compare And Set based atomic update

When to use?

Use compareAndSet, when the lock in the solution described above becomes a bottleneck. Or use it, if there exists a ready-made solution, as for example the AtomicInteger as shown below.

Example

The following implements a counter based on Atomic Integer.
public class AtomicIntegerCounter {
	private final AtomicInteger i = new AtomicInteger();
	public void addOne()
	{
		i.incrementAndGet();
	}	
	public int get()
	{
		return i.get();
	}
}
The Atomic Integer uses compareAndSet Internally in the incrementAndGet method:
public final int incrementAndGet() {
  for (;;) {
  int current = get();
  int next = current + 1;
  if (compareAndSet(current, next))
  return next;
  }
  }

How does it work?

Again declaring the field volatile makes changes made by one thread visible to the other threads. You are optimistically calculating the new value and only set the calculated value when the value of the field is still the same as at the beginning of the calculation. Thereby you make sure that the value is only written if it was not changed by another thread. If your field points to a collection or graph of objects, you must create a copy before your update, similar as copy on write.

How to test?

We can test this by using a multi-threaded test and adding a wait point in vmlens at the compareAndSet method.

5) Benign Data Race

When to use?

Only use this when you can sacrifice correctness for performance.

Example

The following example shows a counter used in to switch between different implementations in the class sun.reflect.NativeMethodAccessorImpl
class NativeMethodAccessorImpl extends MethodAccessorImpl {
  private Method method;
  private DelegatingMethodAccessorImpl parent;
  private int numInvocations;
  NativeMethodAccessorImpl(Method method) {
  this.method = method;
  }
  public Object invoke(Object obj, Object[] args)
  throws IllegalArgumentException, InvocationTargetException
  {
  if (++numInvocations > ReflectionFactory.inflationThreshold()) {
  MethodAccessorImpl acc = (MethodAccessorImpl)
  new MethodAccessorGenerator().
  generateMethod(method.getDeclaringClass(),
  method.getName(),
  method.getParameterTypes(),
  method.getReturnType(),
  method.getExceptionTypes(),
  method.getModifiers());
  parent.setDelegate(acc);
  }
  return invoke0(method, obj, args);
  }
  ...
}

How does it work?

This way of updating the field does neither guarantee that changes are visible in other threads, nor that other threads are not changing the field between an update. But sometimes, as in the above example, you can live with incorrect results for higher performance.

How to test?

You can test this only with a single threaded test since multiple threads lead to non-deterministic behavior.

Conclusion

Which of the 5 ways to update a field in a thread safe way you use, depends on your performance and safety requirements. Independent of which way you use, you should test it. Read more about unit testing multi-threaded software with vmlens and concurrent-junit in a new way to junit test your multithreaded java code . If you have a question or remark please add a comment below.

testing multi-threaded applications on the JVM made easy

LEARN MORE

© 2020 vmlens Legal Notice Privacy Policy