What is the scope of memory flushed or published to various threads when using volatile and synchronized?
Asked Answered
D

1

13

This question is in reference to memory visibility only, not happens-before and happens-after. There are four ways in Java that guarantees changes to memory in one thread to be made visible to another thread. (reference http://gee.cs.oswego.edu/dl/cpj/jmm.html)

  1. A writing thread releases a synchronization lock and a reading thread subsequently acquires that same synchronization lock.
  2. If a field is declared as volatile, any value written to it is flushed and made visible by the writer thread before the writer thread performs any further memory operation (i.e., for the purposes at hand it is flushed immediately).
  3. The first time a thread accesses a field of an object, it sees either the initial value of the field or a value since written by some other thread.
  4. As a thread terminates, all written variables are flushed to main memory.

According to Java Concurrency in Practice, the bible on such questions:

The visibility effects of volatile variables extend beyond the value of the volatile variable itself. When thread A writes to a volatile variable and subsequently thread B reads that same variable, the values of all variables that were visible to A prior to writing to the volatile variable become visible to B after readin the volatile variable.

Volatile question

Does this mean that the JVM actually keeps track of volatile variable reads and writes, in order to know how to flush memory from A to B and not A to C? So A writes to the variable, and later C reads from the variable, and then later B reads from the variable, the flushing is done on a per-thread basis between A and B and A and C, but not B and C? Or, does it imply that all cached memory is flushed, regardless of threads? Are only the volatile variables flushed, or all cached memory?

Synchronized question

For the synchronized keyword flushing, it says that only memory updated inside the lock is guaranteed to be published to other threads. That implies that in the following code, two threads running method(), leaving the synchronized block will flush staticVar2 to the other thread, but not staticVar1, is that correct?

Also, in method2(), synchronizing over differentLock can cause happens-before happens-after problems if another thread is executing method(). However, the question is in terms of visibility. If thread A executes method, then later thread B executes method2(), is the value of staticVar2 published from A to B, even though the two threads don't synchronize over the same lock?

static int staticVar1, staticVar2;
void method() {
    staticVar1++;
    synchronized (lock) {
        staticVar2++;
    }
}
void method2() {
    synchronized (differentLock) {
        staticVar2++;
    }
}

Static question

It appears to me that if staticVar1 is never updated to other threads, then all static variables in any program require a volatile declaration, or should only be accessed in synchronized blocks. That seems rather harsh, but is it correct? I've sure seen a whole lot of static variables in my time that aren't synchronized.

In summary

  1. Do volatile read-writes flush all memory to all threads, or only between the two accessing threads? Whichever the answer, is all memory flushed or only the volatile variables?
  2. Is all changed memory flushed when exiting a synchronized block, or just the memory that was changed within the block? If not all memory is flushed, does the lock object that a thread synchronizes over have to be the same in order to see the value (i.e. does the lock object have any effect on memory visibility)?
  3. Do all static variables accessed by two threads have to be synchronized?
Dinner answered 11/6, 2013 at 2:4 Comment(1)
You spoke my mind buddy. Thanks for such a good writing.Salespeople
J
6

There is no scope limitation in terms of memory. When you have a read or write barrier it applies for all memory reads/writes.

Where I have seen a limitation is in memory mappings. When you memory map a file you have to be careful how you make this available to another threads as this new memory mapping might not be visible in another thread immediately resulting in a BUS error (and a crash of the JVM) This appears to be a OS bug as the newest versions of Linux and Windows don't appear to have this problem.

That implies that in the following code, two threads running method(), leaving the synchronized block will flush staticVar2 to the other thread, but not staticVar1, is that correct?

statixVar1 will always be flushed when staticVar2 is, perhaps sooner. No guarantee as to when, but the order is guaranteed.

If thread A executes method, then later thread B executes method2(), is the value of staticVar2 published from A to B, even though the two threads don't synchronize over the same lock?

Yes, the lock used doesn't matter for the happen-before guarantees.

Do volatile read-writes flush all memory to all threads, or only between the two accessing threads? Whichever the answer, is all memory flushed or only the volatile variables?

All dirty memory is flushed on a write barrier and all reads will be order consistent on a read barrier. volatile performs both a write barrier on a write and a read barrier on a read.

Is all changed memory flushed when exiting a synchronized block, or just the memory that was changed within the block?

All of the memory changed by that thread.

Do all static variables accessed by two threads have to be synchronized?

Only if one thread modifies the variable. Any number of threads can read static values without synchronization.

Jaffna answered 11/6, 2013 at 10:6 Comment(6)
"Yes, the lock used doesn't matter for the happen-before guarantees." => in practice maybe, in theory it does matter.Glaucescent
" > That implies that in the following code, two threads running method(), leaving the synchronized block will flush staticVar2 to the other thread, but not staticVar1, is that correct? > staticVar1 will always be flushed when staticVar2 is, perhaps sooner. No guarantee as to when, but the order is guaranteed." Isn't the guarantee that it will happen at least when the thread exists the synchronized block?Dinner
@PeterLawrey Yes, the lock used doesn't matter for the happen-before guarantees. - I find this plain wrong, happens before works for subsequent actions as per JLS, it should really be No, the lock matters - it MUST be the same lock, according to the JLS and the official java tutorial about concurrency.Sprung
The ordering of the writes to staticVar1 and staticVar2 isn't guaranteed by the JMM. The synchronized block has roach motel semantics which means that the staticVar1 store can jump inside the sync block and can freely be reordered with staticVar2 store. If you would do 2 ordered loads of staticVar2 and staticVar1, you are allowed to observe the 2 stores out of order.Sempach
And as eugene already pointed out, the same lock instance needs to be used. Otherwise there is a data race.Sempach
@Sempach Perhaps you are right in terms of the guarentees the JLS provides however no architecture I know of has such fine grain control over happens before guarantees. There is only one ordering for all memory in x64/ARM processors.Jaffna

© 2022 - 2024 — McMap. All rights reserved.