[英]Lock vs synchronized vs atomic variables vs volatile in java in terms of read-write atomicity, visibility and protection from reordering
I read following about volatile
from the book Java Concurrency in Practice:我从 Java Concurrency in Practice 一书中读到了以下关于volatile
的内容:
When a field is declared volatile, the compiler and runtime are put on notice that this variable is shared and that operations on it should not be reordered with other memory operations.当一个字段被声明为 volatile 时,编译器和运行时会注意到这个变量是共享的,并且对它的操作不应与其他内存操作重新排序。 Volatile variables are not cached in registers or in caches where they are hidden from other processors, so a read of a volatile variable always returns the most recent write by any thread.易失性变量不会缓存在寄存器或缓存中,它们对其他处理器隐藏,因此对易失性变量的读取始终返回任何线程的最新写入。
The visibility effects of volatile variables extend beyond the value of the volatile variable itself. volatile 变量的可见性影响超出了 volatile 变量本身的值。 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 reading the volatile variable.当线程 A 写入一个 volatile 变量,随后线程 B 读取同一个变量时,在写入 volatile 变量之前对 A 可见的所有变量的值在读取 volatile 变量后对 B 可见。 So from a memory visibility perspective, writing a volatile variable is like exiting a synchronized block and reading a volatile variable is like entering a synchronized block.所以从内存可见性的角度来看,写入一个 volatile 变量就像退出一个同步块,读取一个 volatile 变量就像进入一个同步块。
I am confused with the last sentence above.我对上面的最后一句话感到困惑。 Say variable x
is defined volatile
and before modifying x
, u
, v
and w
were visible to thread A
, then when thread B
reads x
afterwards, it will also be able to read latest values of u
, v
and w
.假设变量x
被定义为volatile
并且在修改x
、 u
、 v
和w
之前对线程A
可见,那么当线程B
之后读取x
,它也将能够读取u
、 v
和w
最新值。 Can we specify for same for synchronized
?.我们可以为synchronized
指定相同的吗?。
Q1.一季度。 That is, is below correct?也就是说,下面正确吗?
Variables
u
,v
andw
were visible to threadA
while exitingsynchronized
block, then the latest values ofu
,v
andw
will be visible to threadB
enteringsynchronized
block afterwards.变量u
、v
和w
在退出synchronized
块时对线程A
可见,然后u
、v
和w
的最新值对之后进入synchronized
块的线程B
可见。
I feel above fact is incorrect as u
, v
and w
may be stored in caches and registers as they are not defined volatile
.我觉得上述事实是不正确的,因为u
、 v
和w
可能存储在缓存和寄存器中,因为它们没有被定义为volatile
。 Am I correct with this?我对此是否正确? So visibility is not ensured by synchronized
(and also by locks
and atomic
variables as they are similar to synchronized
)因此, synchronized
不能确保可见性(以及locks
和atomic
变量,因为它们类似于synchronized
)
The book further says:书中进一步说:
Locking can guarantee both visibility and atomicity;加锁可以同时保证可见性和原子性; volatile variables can only guarantee visibility. volatile 变量只能保证可见性。
But I feel following:但我感觉如下:
synchronized
and atomic variables only guarantee read-write atomicity (not visibility and protection from reordering).锁、 synchronized
和原子变量仅保证读写原子性(不可见性和防止重新排序)。volatile
guarantee visibility and protection from reordering by compiler and runtime (not read-write atomicity). volatile
保证可见性并防止编译器和运行时重新排序(不是读写原子性)。Q2. Q2。 Am I correct with above two points?以上两点我是否正确?
1) Locks, synchronized and atomic variables guarantee read-write atomicity and visibility and protection from reordering 1) 锁、同步和原子变量保证读写原子性和可见性以及防止重新排序
2) volatile guarantees visibility and protection from reordering by compiler and runtime 2) volatile 保证可见性并防止编译器和运行时重新排序
read-write atomicity of volatile fields is a little bit tricky: reading and writing to a volatile field is atomic, for example if you write to a volatile long (64 bit) on a 32 bit jvm the read and the write is still atomic. volatile 字段的读写原子性有点棘手:读取和写入 volatile 字段是原子的,例如,如果您在 32 位 jvm 上写入 volatile long(64 位),则读取和写入仍然是原子的。 You always read the complete 64 bit.您总是阅读完整的 64 位。 But operations like ++ on a volatile int or long are not atomic但是像 ++ 对 volatile int 或 long 的操作不是原子的
Hopefully the following is accurate... this is my current as-simple-as-possible-but-no-simpler understanding....希望以下内容是准确的......这是我目前尽可能简单但不简单的理解......
Q1.一季度。 That is, is below correct?也就是说,下面正确吗?
Variables
u
,v
andw
were visible to threadA
while exitingsynchronized
block, then the latest values ofu
,v
andw
will be visible to threadB
enteringsynchronized
block afterwards.变量u
、v
和w
在退出synchronized
块时对线程A
可见,然后u
、v
和w
的最新值对之后进入synchronized
块的线程B
可见。
I'm assuming here by "latest values" you actually mean "latest values at the time the synchronized
block was exited"...我在这里假设“最新值”实际上是指“退出synchronized
块时的最新值”......
Then yes - with the caveat of course that both A
and B
must synchronize on the same object .然后是的 - 当然需要注意的是, A
和B
必须在同一个对象上同步。
Side note: of course, a similar caveat applies to the visibility guarantee of volatile
- A
and B
must write and read (respectively) the same volatile field .旁注:当然,类似的警告适用于volatile
的可见性保证 - A
和B
必须(分别)写入和读取相同的 volatile 字段。
But I feel following:但我感觉如下:
- Locks,
synchronized
and atomic variables only guarantee read-write atomicity (not visibility and protection from reordering).锁、synchronized
和原子变量仅保证读写原子性(不可见性和防止重新排序)。volatile
guarantee visibility and protection from reordering by compiler and runtime (not read-write atomicity).volatile
保证可见性并防止编译器和运行时重新排序(不是读写原子性)。Q2. Q2。 Am I correct with above two points?以上两点我是否正确?
Correct for #2 but incorrect for #1...对#2 正确但对#1 不正确...
synchronized
guarantees visibility as well as atomicity. synchronized
保证了可见性和原子性。 The idea of "visibility" is also described as there being a happens-before relationship. “可见性”的概念也被描述为存在先发生的关系。
In other words, thread A
exiting synchronized (x)
happens-before thread B
entering synchronized (x)
.换句话说,线程A
退出synchronized (x)
发生在线程B
进入synchronized (x)
。
Similarly, a write to volatile
field x
happens-before a read of volatile
field x
.类似地,写入volatile
字段x
发生在读取volatile
字段x
。
In other words, with regards to visibility , synchronized
enter/exit pairs give you exactly the same guarantee as volatile
read/write pairs.换句话说,关于可见性, synchronized
进入/退出对为您提供与volatile
读/写对完全相同的保证。
But synchronized
pairs guarantee both visibility and atomicity, while volatile
pairs guarantee only visibility.但是synchronized
对保证可见性和原子性,而易volatile
对只保证可见性。
Oops - forgot one exception: volatile long
and volatile double
does guarantee that reads and writes of these 64-bit values will be atomic (ie, will avoid "word tearing").糟糕 - 忘记了一个例外: volatile long
和volatile double
确实保证这些 64 位值的读取和写入将是原子的(即,将避免“字撕裂”)。
Another way to look at it: having a volatile
field x
is kind of like having a tiny synchronized (x')
around each read or write of x
, where x'
is some otherwise invisible lock object corresponding to x
(it's not quite the same, because with volatile
you have to pair reads with writes, whereas all synchronized
keywords work the same way).另一种看待它的方式:有一个volatile
字段x
有点像在每次读取或写入x
周围有一个微小的synchronized (x')
,其中x'
是一些与x
对应的其他不可见的锁对象(它并不完全相同,因为使用volatile
您必须将读取与写入配对,而所有synchronized
关键字的工作方式相同)。
I feel above fact is incorrect as u,v and w may be stored in caches and registers as they are not defined volatile.我觉得上述事实是不正确的,因为 u、v 和 w 可能存储在缓存和寄存器中,因为它们没有被定义为 volatile。 Am I correct with this?我对此是否正确?
This is somewhat surprising, but the guarantees of visibility provided by synchronized
and volatile
apply to everything that is visible to the two threads, and are not limited only to the object being locked, the volatile
field itself, other fields in the same object, etc.这有点令人惊讶,但是synchronized
和volatile
提供的可见性保证适用于两个线程可见的所有内容,而不仅限于被锁定的对象、 volatile
字段本身、同一对象中的其他字段等.
That's why it kindof makes sense to think of them in terms of memory barriers, if you are familiar with low level assembly/kernel programming, etc.这就是为什么如果您熟悉低级汇编/内核编程等,从内存屏障的角度考虑它们是有意义的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.