Java concurrency quirks

Java concurrency is surprisingly difficult to get right. Some operations that look atomic can be especially deceptive. For instance, look at the following simple class. It has only one method which does an operation that looks atomic:

public class UniqueNumber {
    // Returns a unique integer value.
    public int get() {
        return ++val;
    }
    private int val;
}

The ++ operator applied to an integer might look like an atomic operation but it can lead to incorrect results if an object of UniqueNumber is shared by multiple threads. Why? Because ++ is not really atomic as we see from the following byte code dump:

> javap -c UniqueNumber
........
........
public int get();
Code:
0: aload_0
1: dup
2: getfield #2; //Field val:I
5: iconst_1
6: iadd
7: dup_x1
8: putfield #2; //Field val:I
11: ireturn

++ gets expanded to VM operations that loads the variable, adds 1 to it and saves the new value back. Now imagine two threads simultaneously calling get() on a UniqueNumber object. (The opcodes executed is shown as comma separated numbers with reference to the above byte code listing and the current stack is shown in square brackets):

Thread1 => 0,1,2 [0]
Thread2 => 0,1,2 [0], 5 [0 1], 6 [1]
Thread1 => 5 [0 1], 6 [1]
.... and so on ....

In the end both threads end up fetching the value 1, which is not what the UniqueNumber class was expected to produce!

Even fetching values directly from “primitive” types can be non-atomic operations at the VM level. The is true for 64-bit types like long and double as a Java implementation (especially on a 32 bit arch) can choose to implement those types using two 32 bit values. Then reading and writing a long/double will get expanded to two VM opcodes. If primitive types like boolean, int, long and double are shared between threads (even if only one thread does all the writing) declare them as volatile.