Is there any point in using a volatile long? Is there any point in using a volatile long? multithreading multithreading

Is there any point in using a volatile long?


Not sure if I understand your question correctly, but the JLS 8.3.1.4. volatile Fields states:

A field may be declared volatile, in which case the Java memory model ensures that all threads see a consistent value for the variable (ยง17.4).

and, perhaps more importantly, JLS 17.7 Non-atomic Treatment of double and long :

17.7 Non-atomic Treatment of double and long
[...]
For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64 bit value from one write, and the second 32 bits from another write. Writes and reads of volatile long and double values are always atomic. Writes to and reads of references are always atomic, regardless of whether they are implemented as 32 or 64 bit values.

That is, the "entire" variable is protected by the volatile modifier, not just the two parts. This tempts me to claim that it's even more important to use volatile for longs than it is for ints since not even a read is atomic for non-volatile longs/doubles.


This can be demonstrated by example

  • constantly toggle two fields, one marked volatile and one not between all bits set and all bits clear
  • read the field values on another thread
  • see that the foo field (not protected with volatile) can be read in an inconsistent state, this never happens to the bar field protected with volatile

Code

public class VolatileTest {    private long foo;    private volatile long bar;    private static final long A = 0xffffffffffffffffl;    private static final long B = 0;    private int clock;    public VolatileTest() {        new Thread(new Runnable() {            @Override            public void run() {                while (true) {                    foo = clock % 2 == 0 ? A : B;                    bar = clock % 2 == 0 ? A : B;                    clock++;                }            }        }).start();        while (true) {            long fooRead = foo;            if (fooRead != A && fooRead != B) {                System.err.println("foo incomplete write " + Long.toHexString(fooRead));            }            long barRead = bar;            if (barRead != A && barRead != B) {                System.err.println("bar incomplete write " + Long.toHexString(barRead));            }        }    }    public static void main(String[] args) {        new VolatileTest();    }}

Output

foo incomplete write ffffffff00000000foo incomplete write ffffffff00000000foo incomplete write fffffffffoo incomplete write ffffffff00000000

Note this only happens for me when running on a 32 bit VM, on 64 bit VM I couldn't get a single error in several minutes.


"volatile" serves multiple purposes:

  • guarantees atomic writes to double/long
  • guarantees that when a thread A sees change in volatile variable made by thread B, thread A can also see all other changes made by thread B before the change to volatile variable (think setting the number of used cells in array after setting the cells themselves).
  • prevents compiler optimization based on assumption that only one thread can change the variable (think tight loop while (l != 0) {}.

Is there more?