Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: elaborated sequential consistency part

...

Program order is the execution order that is expected when a single thread is running the statements sequentially, as written in a method. Even if statements execute in the expected order (program order), caching can prevent the latest values from being reflected in the main memory.

Wiki Markup2. [Sequential consistency|BB. Definitions#sequential consistency]: This property provides a very strong guarantee that the compiler will not optimize away or reorder any statements. It guarantees that the program is free from data races. It also ensures that each access of a variable is atomic and immediately visible to other threads. However, "Sequential consistency and/or freedom from data races still allows errors arising from groups of operations that need to be perceived atomically and are not." \[[JLS 05|AA. Java References#JLS 05]\].

The use of sequential consistency as the sole memory model mechanism makes it easy for a programmer to follow the logic, however, introduces a performance penalty. Implementing synchronization correctly guarantees that all executions of the program are sequentially consistent. Consequently, synchronizing code is more expensive than using volatile variables which guarantee a weaker form of sequential consistency.

Volatile read and write operations cannot be reordered with respect to each other and in addition, as required by the JMM, volatile read and write operations are also not reordered with respect to operations on non-volatile variables.

Using volatile variables guarantees a weaker form of sequential consistency because statements prior to a volatile access appear to occur in program order with respect to the statement that writes to the volatile variable, to a thread that is attempting to read the same volatile variable. The previous statements may, however, be freely reordered and may not be sequential consistent with respect to each other. This makes the cost of using volatile cheaper than synchronizing every piece of code.

For example, consider some statements that are being executed by multiple threads:

Thread 1,2,3...

Statement 1

Statement 2

Statement 3

If statements 1, 2 and 3 are always executed sequentially by all threads as given in this program order, they are sequentially consistent with respect to each other. The sequential consistency property also requires that a read operation in some thread does not see the value of a future write operation taking place in the same or another thread. Similarly, a read operation is guaranteed to see the value of the last write to the variable from any thread.

The use of sequential consistency as the sole memory model mechanism makes it easy for a programmer to reason with the program's logic in a multithreading scenario, however, introduces a performance penalty because the compiler is prohibited from reordering code for performing complex optimizations. Using volatile variables reduces this performance penalty at the cost of strong sequential consistency guarantees.

Consider two threads that are executing some statements:

Thread 1

Statement 1

(statement does not use a volatile variable)

Statement 2

(statement does not use a volatile variable)

Statement 3

(statement is a write to a volatile variable v)

 

 

Thread 2

 

 

Statement 4

(statement is a read of a volatile variable v)

Thread 1 and Thread 2 have a happens-before relationship such that Thread 2 does not start before Thread 1 finishes. This is established by the semantics of volatile accesses. Sequential consistency of volatile accesses provides certain visibility and reordering guarantees:

Visibility

A write to a volatile field happens-before every subsequent read of that field. Statements that occur before the write to the volatile field also happen-before the read of the volatile field.

In the previous example, Statement 3 writes to a volatile variable and statement 4 in the second thread, reads the same volatile variable. The read sees the most recent write (to the same variable v) from statement 3. This may not be true in the happens-before order because a future read can always see the default or previous value of v instead of the one set in the most recent write. This guarantee is provided by the sequential consistency property of volatile accesses.

Reordering

Volatile read and write operations cannot be reordered with respect to each other and in addition, as required by the JMM, volatile read and write operations are also not reordered with respect to operations on non-volatile variables. When reading the volatile variable, the other thread will also see statements occurring before the write to the volatile variable, to have already executed with prior occurrences of volatile and non-volatile fields assuming the assigned values.

In the previous example, statement 4 also sees the statements 1 and 2 to have executed and all their operands with the most-up to date values. However, this does not mean that statements 1 and 2 are sequentially consistent with respect to each other. They may be freely reordered by the compiler. In fact, if statement 1 constituted a read of some variable x, it could see the value of a future write to x in statement 2.

Because the guarantees of code present before the volatile write are weaker than sequentially consistent code, volatile as a synchronization primitive, performs better.

Wiki Markup
"Finally, note
Wiki Markup
"Finally, note that the actual execution order of instructions and memory accesses can be in any order as long as the actions of the thread appear to that thread as if program order were followed, and provided all values read are allowed for by the memory model. This allows the programmer to fully understand the semantics of the programs they write, and it allows compiler writers and virtual machine implementors to perform complex optimizations that a simpler memory model would not permit." \[[JPL 06|AA. Java References#JPL 06]\]. 
A write to a volatile field happens-before every subsequent read of that field. Statements that occur before the write to the volatile field also happen-before the read of the volatile field, from possibly another thread. Consequently, declaring a variable volatile guarantees that writes are always visible to subsequent reads of the variable from any thread. When reading the volatile variable, the other thread will also see statements occurring before the write to the volatile variable, to have already executed with prior occurrences of volatile and non-volatile fields assuming the assigned values.
 that a simpler memory model would not permit." \[[JPL 06|AA. Java References#JPL 06]\]. 

Noncompliant Code Example

...