Java Primitive Thread Safety

Thread Safety in Java is something that developers sometimes take for granted. While Ben Hall and I were fixing a significant bug in an open source tool, we encountered a world of hurt based on a very simple Thread safety mistake.

Fundamentally:Java Primitives are not Thread Safe! After polling the room of developers it became obvious that the misunderstanding was actually quite common.

Question:Which statement is thread safe for arithmetic on the primitive “int i”?

int i;
   i = i + 1;
   //or
   i++;

Answer:Neither! Just because “i++;” is one statement does not mean it is atomic and thread-safe!

The open source tool actually took much care in protecting against the “significant objects” (maps, lists, etc.) but ignored the basics. This made for indeterministic results when the tool was under load. If you need to perform some simple arithmetic on a primitive you must protect it just like you protect everything else. (synchronized keyword) The “java.util.concurrent.atomic” package contains some thread-safe primitive implementations that actually are a lot cleaner to use. AtomicInteger() for example contains two basic methods that are thread-safe.

public int addAndGetDelta(int i) {
...
}
public int get() {
...
}

Here is some simple code just in case you have your doubts.
I ran it 3 times here is the output:

  • Primitive int Value=7 AtomicInteger Value=0
  • Primitive int Value=6 AtomicInteger Value=0
  • Primitive int Value=13 AtomicInteger Value=0
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class PrimitiveTesting {
private class IntRunner implements Runnable {
	public void run() {
		try {
			//need a yield
			Thread.sleep(1);
		} catch (InterruptedException ignore) {}
		//adjust the int
		i++;
		i--;
		//adjust the Atomic int
		ai.addAndGet(1);
		ai.addAndGet(-1);
	}
}

	private int i;
	private AtomicInteger ai;
	private ExecutorService executor=Executors.newCachedThreadPool();

	public void testPrimitive() throws InterruptedException {
		i=0;
		ai = new AtomicInteger(0);
		for (int i = 0; i < 1000; i++) {
			executor.execute(new IntRunner());
		}
		executor.shutdown();
		executor.awaitTermination(1L, TimeUnit.SECONDS);

		System.out.println("Primitive int Value=" + i);
		System.out.println("AtomicInteger Value=" + ai.get());
	}

	public static void main(String[] args) throws InterruptedException {
		PrimitiveTesting test = new PrimitiveTesting();
		test.testPrimitive();
	}
}

This would make for a great interview question!
cheers…
Kevin

It's only fair to share...
Share on FacebookGoogle+Tweet about this on TwitterShare on LinkedIn

Leave a Reply