Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what is the reasoning behind volatile semantics in Java and C#

Both C# and Java define that
* volatile reads have acquire semantics
* volatile writes have release semantics

My questions are:

  1. Is this the only correct way to define volatile.
  2. If not, will things be awfully different if the semantics were reversed, that is
    • volatile reads have release semantics
    • volatile writes have acquire semantics
like image 712
yash Avatar asked Jul 05 '12 22:07

yash


People also ask

What is the purpose of volatile keyword in Java?

For Java, “volatile” tells the compiler that the value of a variable must never be cached as its value may change outside of the scope of the program itself.

What happens when a volatile variable is declared in Java programming?

By declaring the counter variable volatile all writes to the counter variable will be written back to main memory immediately. Also, all reads of the counter variable will be read directly from main memory. Declaring a variable volatile thus guarantees the visibility for other threads of writes to that variable.

What happens when a variable is marked as volatile?

volatile has semantics for memory visibility. Basically, the value of a volatile field becomes visible to all readers (other threads in particular) after a write operation completes on it. Without volatile , readers could see some non-updated value.

Where are volatile variables stored in Java?

Volatile fields are instance or class (static) variables and are stored in the heap.


2 Answers

The reasoning behind the volatile semantic is rooted in the Java Memory Model, which is specified in terms of actions:

  • reads and writes to variables
  • locks and unlocks of monitors
  • starting and joining with threads

The Java Memory Model defines a partial ordering called happens-before for the actions which can occur in a Java program. Normally there is no guarantee, that threads can see the results of each other actions.

Let's say you have two actions A and B. In order to guarantee, that a thread executing action B can see the results of action A, there must be a happens-before relationship between A and B. If not, the JVM is free to reorder them as it likes.

A program which is not correctly synchronized might have data races. A data race occurs, when a variable is read by > 1 threads and written by >= 1 thread(s), but the read and write actions are not ordered through the happens-before ordering.

Hence, a correctly synchronized program has no data races, and all actions within the program happen in a fixed order.

So actions are generally only partially ordered, but there is also a total order between:

  • lock acquisition and release
  • reads and writes to volatile variables

These actions are totally ordered.

This makes it sensible to describe happens-before in terms of "subsequent" lock acquisitions and reads of volatile variables.

Regarding your questions:

  1. With the happen-before relationship you have an alternative definition of volatile
  2. Reversing the order would not make sense to the definition above, especially since there is a total order involved.

happens-before

This illustrates the happens-before relation when two threads synchronize using a common lock. All the actions within thread A are ordered by the program order rule, as are the actions within thread B. Because A releases lock M and B subsequently acquires M, all the actions in A before releasing the lock are therefore ordered before the actions in B after acquiring the lock. When two threads synchronize on different locks, we can't say anything about the ordering of actions between themthere is no happens-before relation between the actions in the two threads.

Source: Java Concurrency in Practice

like image 87
Konrad Reiche Avatar answered Nov 15 '22 18:11

Konrad Reiche


The power of the acquire/release semantics isn't so much about how soon other threads see the newly written value of the volatile field itself, but rather in the way volatile operations establish a happens-before relation across different threads. If a thread A reads a volatile field and sees a value that was written to that field in another thread B then thread A is also guaranteed to see values written to other (not necessarily volatile) variables by thread B before the point where it did the volatile write. This looks like cache flushing but only from the point of view of a thread that read the volatile, other threads that don't touch the volatile field have no ordering guarantees with respect to B and might see some of its earlier non-volatile writes but not others if the compiler/JIT is so inclined.

Monitor acquires/releases are similarly characterised by their induced happens-before relation - actions by one thread before a release of a monitor are guaranteed to be visible after a subsequent acquire of the same monitor by another thread. Volatiles give you the same ordering guarantees as monitor synchronisation but without blocking.

like image 22
Ian Roberts Avatar answered Nov 15 '22 18:11

Ian Roberts