4

Writes and reads to a volatile field prevent reordering of reads/writes before and after the volatile field respectively. Variable reads/writes before a write to a volatile variable can not be reordered to happen after it, and reads/writes after a read from a volatile variable can not be reordered to happen before it. But what is the scope of this prohibition? As I understand volatile variable prevents reordering only inside the block where it is used, am I right?

Let me give a concrete example for clarity. Let's say we have such code:

int i,j,k;
volatile int l;
boolean flag = true;

void someMethod() {
    int i = 1;
    if (flag) {
        j = 2;
    }
    if (flag) {
        k = 3;
        l = 4;
    }
}

Obviously, write to l will prevent write to k from reordering, but will it prevent reordering of writes to i and j in respect to l? In other words can writes to i and j happen after write to l?

UPDATE 1

Thanks guys for taking your time and answering my question - I appreciate this. The problem is you're answering the wrong question. My question is about scope, not about the basic concept. The question is basically how far in code does complier guarantee the "happens before" relation to the volatile field. Obviously compiler can guarantee that inside the same code block, but what about enclosing blocks and peer blocks - that's what my question is about. @Stephen C said, that volatile guarantees happen before behavior inside the whole method's body, even in the enclosing block, but I can not find any confirmation to that. Is he right, is there a confirmation somewhere?

Let me give yet another concrete example about scoping to clarify things:

setVolatile() {
   l = 5;
} 

callTheSet() {
   i = 6;
   setVolatile();
}

Will compiler prohibit reordering of i write in this case? Or maybe compiler can not/is not programmed to track what happens in other methods in case of volatile, and i write can be reordered to happen before setVolatile()? Or maybe compiler doesn't reorder method calls at all?

I mean there is got to be a point somewhere, when compiler will not be able to track if some code should happen before some volatile field write. Otherwise one volatile field write/read might affect ordering of half of a program, if not more. This is a rare case, but it is possible.

Moreover, look at this quote

Under the new memory model, it is still true that volatile variables cannot be reordered with each other. The difference is that it is now no longer so easy to reorder normal field accesses around them.

"Around them". This phrase implies, that there is a scope where volatile field can prevent reordering.

Nik Kotovski
  • 141
  • 8
  • Reordering with respect to what? Each other? The write to `l`? Something else? – Stephen C Aug 02 '18 at 07:40
  • In respect to 'l' of course. I thought that was clear, but I am gonna make it more clear. Thanks. – Nik Kotovski Aug 02 '18 at 07:48
  • @NikKotovski Are you asking whether or not `i` or `j` can be re-ordered below the write to `l`? – John Vint Aug 02 '18 at 16:31
  • I like to think this of a barrier and operations that can jump (reordered) down/up (in the order or reading code). this might help https://stackoverflow.com/questions/45151763/analyzing-of-x86-output-generated-by-jit-in-the-context-of-volatile. but to answer your question these are independent reads and *above* the `l` (barrier) anything can be re-ordered. Point is that some other thread seems the write of `l` (observes it), they will also observer the write it `i` and `j` - thus you don't really care about this internal reordering (if it happened) – Eugene Aug 03 '18 at 10:55
  • @JohnVint Yes. Since they're in other blocks, `i` is even in enclosing block, can they be reordered to happen after `l`? – Nik Kotovski Aug 04 '18 at 08:43

4 Answers4

5

Obviously, write to l will prevent write to k from reordering, but will it prevent reordering of writes to i and j?

It is not entirely clear what you mean by reordering; see my comments above.

However, in the Java 5+ memory model, we can say that the writes to i and j that happened before the write to l will be visible to another thread after it has read l ... provided that nothing writes i and j after write to l.

This does have the effect of constraining any reordering of the instructions that write to i and j. Specifically, they can't be moved to after the memory write barrier following the write to l, because that could lead them to not being visible to the second thread.

But what is the scope of this prohibition?

There isn't a prohibition per se.

You need to understand that instructions, reordering and memory barriers are just details of a specific way of implementing the Java memory model. The model is actually defined in terms of what is guaranteed to be visible in any "well-formed execution".

As I understand volatile prevents reordering inside the block where it is used, am I right?

Actually, no. The blocks don't come into the consideration. What matters is the (program source code) order of the statements within the method.


@Stephen C said, that volatile guarantees happen before behavior inside the whole method's body, even in the enclosing block, but I can not find any confirmation to that.

The confirmation is JLS 17.4.3. It states the following:

Among all the inter-thread actions performed by each thread t, the program order of t is a total order that reflects the order in which these actions would be performed according to the intra-thread semantics of t.

A set of actions is sequentially consistent if all actions occur in a total order (the execution order) that is consistent with program order, and furthermore, each read r of a variable v sees the value written by the write w to v such that:

  • w comes before r in the execution order, and

  • there is no other write w' such that w comes before w' and w' comes before r in the execution order.

Sequential consistency is a very strong guarantee that is made about visibility and ordering in an execution of a program. Within a sequentially consistent execution, there is a total order over all individual actions (such as reads and writes) which is consistent with the order of the program, and each individual action is atomic and is immediately visible to every thread.

If a program has no data races, then all executions of the program will appear to be sequentially consistent.

Notice that there is NO mention of blocks or scopes in this definition.

Community
  • 1
  • 1
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • `Actually, no. The blocks don't come into the consideration. What matters is the (program source code) order of the statements within the method.` So the scope is method's code block then. Also could you find any proof please? I am not trying to be sacrastic, just don't trust proofless answers. People give lots of wrong answers here. And of course I wouldn't come here if I was capable of finding proof myself. – Nik Kotovski Aug 02 '18 at 08:29
  • The proof is the JLS. Specifically Chapter 17.4.3 through 17.4.5. If you really, really want to understand the JMM, that is where it is specified. I could construct a specific proof for your example using the JMM terminology ... but you are better off just reading it for yourself. https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html. And when you do, you will see that the concepts of "block" and "scope" do come into consideration. – Stephen C Aug 02 '18 at 08:49
  • Sorry ... I meant they do NOT come into consideration. – Stephen C Aug 02 '18 at 09:02
3

EDIT 2

The volatile ONLY gaurentee the happens-before relation.

Why it reorder in single thread

Considered we have two fields:

int i = 0;
int j = 0;

We have a method to write them

void write() {
  i = 1;
  j = 2;
}

As you know, compiler may reorder them. That is because compiler think it is not matter access which first. Because in single thread, they are 'happen together'.

Why can't reorder in multi thread

But now we have another method to read them in another thread:

void read() {
  if(j==2) {
    assert i==1;
  }
}

If compiler still reorder it, this assert may fail. That means j has been 2, but i unexpectly is not 1. Which seems i=1 is happens after assert i==1.

What volatile do

The volatile only gaurantee the happens-before relation.

Now we add volatile

volatile int j = 0;

When we observe j==2 is true, that means j=2 is happened and i=2 is before it, it must happened. So the assert will never fail now.

'Prventing reorder' is just an approach that compiler to provide that guarantee.

Conclusion

The only things you should now is happens-before. Please refer to the link below of java specification. The reordering or not is just a side effect of this guarantee.

Answer for you question

Since l is volatile, acccess to i and j always before access to l in the someMethod. The fact is, every thing before the line l=4 will happen before before it.

EDIT 1

Since the post has been edit. Here is further explasion.

A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.

happens-before means:

If one action happens-before another, then the first is visible to and ordered before the second.

So the access to i and j happen-before access to l.

reference: https://docs.oracle.com/javase/specs/jls/se10/html/jls-17.html#jls-17.4.5

Origin answer

No, the volatile only protect itself, though it is not easy to reorder field access near volatile.

Under the new memory model, it is still true that volatile variables cannot be reordered with each other. The difference is that it is now no longer so easy to reorder normal field accesses around them. Writing to a volatile field has the same memory effect as a monitor release, and reading from a volatile field has the same memory effect as a monitor acquire. In effect, because the new memory model places stricter constraints on reordering of volatile field accesses with other field accesses, volatile or not, anything that was visible to thread A when it writes to volatile field f becomes visible to thread B when it reads f.

The volatile keyword only guarantee that:

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

reference: http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile

Dean Xu
  • 4,438
  • 1
  • 17
  • 44
  • `Reads from and writes to other variables cannot be reordered to occur after a write to a volatile variable, if the reads / writes originally occurred before the write to the volatile variable. The reads / writes before a write to a volatile variable are guaranteed to "happen before" the write to the volatile variable.` [Source.](http://tutorials.jenkov.com/java-concurrency/volatile.html) Also please take a look at @Stephen C answer and our discussion with him. – Nik Kotovski Aug 02 '18 at 08:35
  • 1
    @NikKotovski The reference is right. But your question is `will it prevent reordering of writes to i and j?`, the answer is `no`. – Dean Xu Aug 02 '18 at 08:48
  • (@NikKotovski - I note you haven't corrected the ambiguity in your question yet. Dean is interpreting your question as meaning reordering writes to `i` with respect to writes `j`. This actually is the *obvious* interpretation *to me* as well.) – Stephen C Aug 02 '18 at 08:55
  • While the FAQ is correct, it is not definitive. The JLS is definitive, and also more rigorous. – Stephen C Aug 02 '18 at 08:57
  • @Stephen C you might be right, Dean Xu might be answering about `i` and `j` in respect to each other. While I am just trying to define what "near" or "around them" means exactly in the specifications. – Nik Kotovski Aug 02 '18 at 09:06
  • I am sorry, but I think this quote `A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.` is completely unrelated to my question, because it doesn't mention other fields at all - only the volatile field in question. – Nik Kotovski Aug 02 '18 at 09:11
  • 1
    @NikKotovski Why you think they are not related? I have told that `volatile` ONLY gauratee `the happen-before relation`. I will update my answer later to give you a sample. And that will be my last modification, if you have any question then, please refer the java specification. – Dean Xu Aug 02 '18 at 09:42
  • I can't make the explanation any more clear. I will try, though. The last quote only touches the volatile field itself, while I am curious to know how volatile variable affects OTHER fields. Again specifically what do words `near` and `around them` in your original post mean exactly. – Nik Kotovski Aug 02 '18 at 09:54
  • @NikKotovski forget about the words “near” or “around them”. The effect of a `volatile` write can be global, under the right circumstances. The point about the *happens-before* relationship is that the guarantees are only made for the action of the writing thread prior to the write and actions of a subsequently reading thread, after that read. In other words, other threads still may perceive the other field’s writes out of order, if they don’t read the `volatile` field first *and* that read is *subsequent* to the write. – Holger Aug 02 '18 at 15:42
1

I am curious to know how volatile variable affects OTHER fields

Volatile variables do affect the other fields. JIT compiler can reorder the instructions if he thinks that reordering will not have any impact on the execution output. So if you have 6 independent variable stores JIT can reorder the instructions.

However if you make a variable volatile i.e. in your case variable l then JIT will not reorder any variable STORES after the volatile STORE. And I think that makes sense because in a multithreaded program if I get the value of variable l as 4, then I should get i as 1, because in my program i was written before l and which eventually is Program Order Semantics (If I am not wrong).

Note that volatile variables does two things:
  1. Compiler will not reorder any stores after volatile store / not reorder any reads before volatile read.
  2. Flushes the Load/Store buffer so that all the processor can see the changes.

EDIT:

Good blog here: http://jpbempel.blogspot.com/2013/05/volatile-and-memory-barriers.html

Neerav Vadodaria
  • 317
  • 1
  • 11
  • `Volatile variables doesn't affect the other fields` So you're saying that volatile keyword prevents operations on other fields from being reordered to happen after volatile write, yet you're saying that volatile doesn't affect them (other fields). Does everything here look right to you? – Nik Kotovski Aug 04 '18 at 02:46
-2

Maybe I know the "real scope" you are in dout.

Two types of reorder is the main reason of unordering instruction result: 1. Compiler optimization 2. Cpu processor recordering(maily caused by cache and main memory synchronize)

volatile keyword first need to confirm the flushing of volatile variable, at the meantime, other variables are also flushed to main memory.But because of compiler reordering, some writable instructions before the volatile valatile variable may be reordered after the volatile variable, the reader may be confused to read not the real time other variable values which is before the volatile variable in program order, so the rule of "variables writting instruction before the volatile variable is forced to run before the volatile" is made.This optimazation is done by Java Compiler or JIT.

The main point is optimization of compiler in instructions,like finding dead code , instruction reorder operation, the instructions code range is always a "basic block"(Except some other constant propagation optimization, etc.). A basic block is an set of instructions without jmp instruction inside, so this is a basic block. So in my opinion, the reorder operation is fixed in the range basic block. the basic block in source code is always a block or the body of a method.

And also because java does not have inline function, the method call is used by dynamic invoke method instruction, the reorder operation should not be across two method.

So, the scope will not be larger than a "method body", or maybe only a area of "for" body , it's the basic block range.

This is all my thought, I'm not sure if it is right, someone can help to make it more accurate.

Frio
  • 160
  • 1
  • 7