Problems with understanding the semantics of volatility in Java

I read about using volatile variables in Java. I understand that they provide instant visibility of their latest updates for all threads running in the system on different cores / processors. However, the atomicity of the operations that caused these updates is not ensured. I often use the following literature

It is recorded in an unstable field - before each reading of the same field.

That's where I got a little confused. Here is a piece of code that should help me better explain my request.

 volatile int x = 0; volatile int y = 0; Thread-0: | Thread-1: | if (x==1) { | if (y==1) { return false; | return false; } else { | } else { y=1; | x=1; return true; | return true; } | } 

Since x and y are both volatile , we do the following: before the edges

  • between writing y to Thread-0 and reading y to Thread-1
  • between writing x to Thread-1 and reading x to Thread-0

Does this mean that at any given time only one of the threads can be in the "else" block (since the record should have happened before reading)?

It is entirely possible that Thread-0 starts, loads x, finds its value as 0, and right before it is going to write y in an else block, there is a context switch in Thread-1 that loads y, finds its value equal to 0 and thus enters the else block. Does volatile against such context switches (seems very unlikely)?

+4
source share
7 answers

So, I think this question is a little weedy, the bottom line is that volatile indicates that the value of a variable can change outside the scope of the current stream and that its value should always be read before use.

Basically, the statement you are quoting does indeed mean that the value will be read before the value is replaced with the current stream.

Your example is a race condition, both threads can return true, cannot return true, or they can return a different value - volatile semantics will not determine the execution for your example (I would recommend that you compile and run it and see that the output changes) .

A common way to illustrate the behavior of volatile is to start two threads, when one thread updates the general state and sees what happens when the field is checked and when not:

 class VolatileTest implements Runnable { // try with and without volatile private volatile boolean stopRunning = false; public void triggerStop(){ stopRunning = true; } @Override public void run(){ while(!stopRunning); System.out.println("Finished."); } public static void main (String[] args) throws java.lang.Exception { final VolatileTest test = new VolatileTest(); new Thread(test).start(); Thread.sleep(1000); test.triggerStop() = false; } } 

In this example, a failure of the stopRunning label as volatile can cause the while to continue forever, because if stopRunning not marked as volatile , it does not need to read the value at each iteration.

+5
source

Semantics of the Volatile

The problem you are referring to is a variant of the Dekkers algorithm . Google with a lot of details about various implementations and detailed information about them.

If two processes try to enter the critical section at the same time, the algorithm will allow only one process, depending on whose queue it is. If one process is already in a critical section, another process will be busy waiting for the completion of the first process. This is done using two flags flag [0] and flag [1], which indicate the intention to enter the critical section, and a rotation variable that indicates who takes precedence between the two processes.

Wikipedia talks about the relevance of volatile with the help of the Decloger Alogrit

Mutable information

But I found that this article perfectly explains volatile in one sentence.

If the variable is declared as volatile, then it is guaranteed that any stream that reads the field will see the last recorded value. ( Lars Vogel , 2008)

In fact, volatile is used to indicate that the value of a variable will be changed by different threads. ( javamex , 2012)

University of Messi: Parallel Lecture on Volatility

Massey lecture slide
(source: iforce.co.nz )

Source: Professor Hans W. Gesgen

If you haven't figured out volatile yet, see how atomicity works.

Hope this helps! :)

+2
source

Does this mean that at any given time only one of the threads can be in the "else" block (since the record should have happened before reading)?

No no.

Is protection against such context switches really volatile (seems very unlikely)?

No, this does not protect against this. The race condition that you have identified does exist.

This indicates that volatile components are not a general purpose synchronization mechanism. Rather, they avoid synchronization in certain situations that revolve around a single variable that can be read or written in a single memory operation.

There are two variables in your example ...

+1
source

I think you have a bad example here. Both threads can easily fall into the else block (thread 0 reads x and checks for 1, then thread 1 reads y and checks for 1).

An example of interest looks something like this:

 thread 0 thread 1 x = 1 y = 1 if (y == 0) { if (x == 0) { } } 

Both threads cannot fall into the if block if x and y are unstable.

0
source

In the Java Language Specification, the volatile field has the following properties:

A field can be declared mutable, in which case the stream must coordinate its working copy of the field with the main copy each time the variable is accessed. In addition, operations on the main copies of one or more variable variables on behalf of the stream are performed by the main memory in the exact order in which the stream is requested.

From the foregoing,

Does this mean that at any given time only one of the threads can be in the "else" block (since the record should have happened before reading)?

This will not happen, because volatile ensures that the stream will read the last value of the variable in memory, and not from the local copy. There is no possibility of blocking by declaring the variable as mutable. You must use the concept of synchronized to serialize writing to a variable.

0
source

all mutable reads and writes for all variables are in perfect order (synchronization order).

Writing to the volatile v variable occurs before all subsequent ones read v by any stream (where the "subsequent" is determined in accordance with the synchronization order)

http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.4

0
source

Flying visibility guarantees, synchronization guarantees visibility and atomicity

Only guarantee this, if x or y updated by one thread, then the update will be visible to all threads. The order of execution depends only on how your threads work. The answer to the question of context switching is no, since the comparison occurs before input, so it will be only once, and it will be based on the value of the variable at that time. This is possible if you have several comparisons for the same variable.

The best way to use volatile is to maintain class state variables. Where in any new assignment will be displayed in all threads that refer to this class

If you look at the source code of ThreadPoolExecutor , some state variables will be unstable.

One of them is runState , which determines the state of ThreadPoolExecutor . Now when shutdown() is called runState , it is updated to SHUTDOWN , so the methods that accept the execute submit task stop accepting the task, which turns out to be simple if(runState==RUNNING)

Below is the execute code of the ThreadPoolExecutor method

In the code below, if you do not see any additional synchronization for poolSize , runState , since they are mutable state variables.

 public void execute(Runnable command) { if (command == null) throw new NullPointerException(); if (**poolSize** >= corePoolSize || !addIfUnderCorePoolSize(command)) {//poolSize volatile if (**runState** == RUNNING && workQueue.offer(command)) {//runstate volatile if (**runState** != RUNNING || **poolSize** == 0)//runstate volatile ensureQueuedTaskHandled(command); } else if (!addIfUnderMaximumPoolSize(command)) reject(command); // is shutdown or saturated } } 

When not to use it?

If a variable is to be used in atomic operations or when one value of a variable depends on another value of the variable, for example. Increment i ++.

Alternative

Atomic variables are better called volatile , which have memory semantics as mutable, but provide you with additional operations. e.g. incrementAndGet () provided by AtomicInteger.

0
source

Source: https://habr.com/ru/post/1435613/


All Articles