What does "subsequent read" mean in the context of volatile variables? What does "subsequent read" mean in the context of volatile variables? multithreading multithreading

What does "subsequent read" mean in the context of volatile variables?


It sounds to me like it's saying that it provides lockless acquire/release memory-ordering semantics between threads. See Jeff Preshing's article explaining the concept (mostly for C++, but it main point of the article is language neutral and about the concept.)

In fact Java volatile provides sequential consistency, not just acq/rel. There's no actual locking, though. See Jeff Preshing's article for an explanation of why the naming matches what you'd do with a lock.)


If a reader sees the value you wrote, then it knows that everything in the producer thread before that write has also already happened.

This ordering guarantee is only useful in combination with other guarantees about ordering within a single thread.

e.g.

int data[100];volatile bool data_ready = false;

Producer:

data[0..99] = stuff; // release store keeps previous ops above this linedata_ready = true;

Consumer:

while(!data_ready){}     // spin until we see the write// acquire-load keeps later ops below this lineint tmp = data[99];      // gets the value from the producer

If data_ready was not volatile, reading it wouldn't establish a happens-before relationship between two threads.

You don't have to have a spinloop, you could be reading a sequence number, or an array index from a volatile int, and then reading data[i].


I don't know Java well. I think volatile actually gives you sequential-consistency, not just release/acquire. A sequential-release store isn't allowed to reorder with later loads, so on typical hardware it needs an expensive memory barrier to make sure the local core's store buffer is flushed before any later loads are allowed to execute.

Volatile Vs Atomic explains more about the ordering volatile gives you.

Java volatile is just an ordering keyword; it's not equivalent to C11 _Atomic or C++11 std::atomic<T> which also give you atomic RMW operations. In Java, volatile_var++ is not an atomic increment, it a separate load and store, like volatile_var = volatile_var + 1. In Java, you need a class like AtomicInteger to get an atomic RMW.

And note that C/C++ volatile doesn't imply atomicity or ordering at all; it only tells the compiler to assume that the value can be modified asynchronously. This is only a small part of what you need to write lockless for anything except the simplest cases.


It means that once a certain Thread writes to a volatile field, all other Thread(s) will observe (on the next read) that written value; but this does not protect you against races though.

Threads have their caches, and those caches will be invalidated and updated with that newly written value via cache coherency protocol.

EDIT

Subsequent means whenever that happens after the write itself. Since you don't know the exact cycle/timing when that will happen, you usually say when some other thread observes the write, it will observer all the actions done before that write; thus a volatile establishes the happens-before guarantees.

Sort of like in an example:

 // Actions done in Thread A int a = 2; volatile int b = 3; // Actions done in Thread B if(b == 3) { // observer the volatile write    // Thread B is guaranteed to see a = 2 here }

You could also loop (spin wait) until you see 3 for example.


I'm confused what does subsequent means in context of multithreading. Does this sentence implies some global clock for all processors and cores...?

Subsequent means (according to the dictionary) coming after in time. There certainly is a global clock across all CPUs in a computer (think X Ghz) and the document is trying to say that if thread-1 did something at clock tick 1 then thread-2 does something on another CPU at clock tick 2, it's actions are considered subsequent.

A write to a volatile field happens-before every subsequent read of that same field.

The key phrase that could be added to this sentence to make it more clear is "in another thread". It might make more sense to understand it as:

A write to a volatile field happens-before every subsequent read of that same field in another thread.

What this is saying that if a read of a volatile field happens in Thread-2 after (in time) the write in Thread-1, then Thread-2 will be guaranteed to see the updated value. Further up in the documentation you point to is the section (emphasis mine):

... The results of a write by one thread are guaranteed to be visible to a read by another thread only if the write operation happens-before the read operation. The synchronized and volatile constructs, as well as the Thread.start() and Thread.join() methods, can form happens-before relationships. In particular.

Notice the highlighted phrase. The Java compiler is free to reorder instructions in any one thread's execution for optimization purposes as long as the reordering doesn't violate the definition of the language – this is called execution order and is critically different than program order.

Let's look at the following example with variables a and b that are non-volatile ints initialized to 0 with no synchronized clauses. What is shown is program order and the time in which the threads are encountering the lines of code.

Time     Thread-1        Thread-21        a = 1;          2        b = 2;          3                        x = a;4                        y = b;5        c = a + b;      z = x + y;

If Thread-1 adds a + b at Time 5, it is guaranteed to be 3. However, if Thread-2 adds x + y at Time 5, it might get 0, 1, 2, or 3 depends on race conditions. Why? Because the compiler might have reordered the instructions in Thread-1 to set a after b because of efficiency reasons. Also, Thread-1 may not have appropriately published the values of a and b so that Thread-2 might get out of date values. Even if Thread-1 gets context-switched out or crosses a write memory barrier and a and b are published, Thread-2 needs to cross a read barrier to update any cached values of a and b.

If a and b were marked as volatile then the write to a must happen-before (in terms of visibility guarantees) the subsequent read of a on line 3 and the write to b must happen-before the subsequent read of b on line 4. Both threads would get 3.

We use volatile and synchronized keywords in java to ensure happens-before guarantees. A write memory barrier is crossed when assigning a volatile or exiting a synchronized block and a read barrier is crossed when reading a volatile or entering a synchronized block. The Java compiler cannot reorder write instructions past these memory barriers so the order of updates is assured. These keywords control instruction reordering and insure proper memory synchronization.

NOTE: volatile is unnecessary in a single-threaded application because program order assures the reads and writes will be consistent. A single-threaded application might see any value of (non-volatile) a and b at times 3 and 4 but it always sees 3 at Time 5 because of language guarantees. So although use of volatile changes the reordering behavior in a single-threaded application, it is only required when you share data between threads.