简体   繁体   English

在读写原子性、可见性和防止重新排序方面,锁定、同步、原子变量与 Java 中的 volatile

[英]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并且在修改xuvw之前对线程A可见,那么当线程B之后读取x ,它也将能够读取uvw最新值。 Can we specify for same for synchronized ?.我们可以为synchronized指定相同的吗?。

Q1.一季度。 That is, is below correct?也就是说,下面正确吗?

Variables u , v and w were visible to thread A while exiting synchronized block, then the latest values of u , v and w will be visible to thread B entering synchronized block afterwards.变量uvw在退出synchronized块时对线程A可见,然后uvw的最新值对之后进入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 .我觉得上述事实是不正确的,因为uvw可能存储在缓存和寄存器中,因为它们没有被定义为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不能确保可见性(以及locksatomic变量,因为它们类似于synchronized

The book further says:书中进一步说:

Locking can guarantee both visibility and atomicity;加锁可以同时保证可见性和原子性; volatile variables can only guarantee visibility. volatile 变量只能保证可见性。

But I feel following:但我感觉如下:

  1. Locks, synchronized and atomic variables only guarantee read-write atomicity (not visibility and protection from reordering).锁、 synchronized和原子变量仅保证读写原子性(不可见性和防止重新排序)。
  2. 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 and w were visible to thread A while exiting synchronized block, then the latest values of u , v and w will be visible to thread B entering synchronized block afterwards.变量uvw在退出synchronized块时对线程A可见,然后uvw的最新值对之后进入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 .然后是的 - 当然需要注意的是, AB必须在同一个对象上同步。

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的可见性保证 - AB必须(分别)写入和读取相同的 volatile 字段

But I feel following:但我感觉如下:

  1. Locks, synchronized and atomic variables only guarantee read-write atomicity (not visibility and protection from reordering).锁、 synchronized和原子变量仅保证读写原子性(不可见性和防止重新排序)。
  2. 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 longvolatile 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.这有点令人惊讶,但是synchronizedvolatile提供的可见性保证适用于两个线程可见的所有内容,而不仅限于被锁定的对象、 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM