我从《实践中的Java并发》(Java Concurrency in Practice)一书中读到了以下关于volatile的内容:
当一个字段被声明为volatile时,编译器和运行时会注意到这个变量是共享的,对它的操作不应该与其他内存操作一起重新排序。易失性变量不会缓存在寄存器或缓存中,在这些寄存器或缓存中它们对其他处理器是隐藏的,因此对易失性变量的读取总是返回任何线程最近的写入。
可变变量的可见性影响超出了可变变量本身的值。当线程A写入易失性变量,然后线程B读取该变量时,在写入易失性变量之前对A可见的所有变量的值在读取易失性变量后对B可见。因此,从内存可见性的角度来看,写入易失性变量就像退出同步块,读取易失性变量就像进入同步块。
我对上面最后一句话感到困惑。假设变量x
被定义为volatile
,在修改x
之前,u
,v
和w
对线程A
可见,那么当线程B
读取x
之后,它还能够读取u
,v
和w
的最新值。我们可以为同步的指定相同的吗?。
问题1。也就是说,是否低于正确值?
当退出
synchronized
块时,线程A
可以看到变量u
、v
和w
,然后线程B
进入synchronized
块时,线程A
可以看到u
、v
和w
的最新值。
我觉得上述事实是不正确的,因为
u
,v
和w
可能存储在缓存和寄存器中,因为它们没有定义volatile
。我说的对吗?因此,同步
(以及锁
和原子
变量,因为它们类似于同步
),无法确保可见性
这本书还说:
锁定可以保证可见性和原子性;易变变量只能保证可见性。
但我觉得:
锁、synchronized
和原子变量只保证读写原子性(而不是可见性和防止重新排序)
问题2。以上两点我说得对吗?
希望以下是准确的...这是我目前尽可能简单但不简单的理解......
问题1。也就是说,是否低于正确值?
当退出synchronized
块时,线程A
可以看到变量u
、v
和w
,然后线程B
进入synchronized
块时,线程A
可以看到u
、v
和w
的最新值。
我假设这里的“最新值”实际上是指“退出同步
块时的最新值”...
然后是的——当然要注意的是,A
和B
必须在同一个对象上同步。
附带说明:当然,类似的警告适用于可见性保证的易失性
-A
和B
必须写入和读取(分别)相同的易失性字段。
但我觉得:
synchronized
和原子变量只保证读写原子性(而不是可见性和防止重新排序)
volatile
保证可见性,防止编译器和运行时重新排序(而不是读写原子性)
问题2。以上两点我说得对吗?
对#2正确但对#1不正确...
同步的
保证了可见性和原子性。“可见性”的概念也被描述为发生在发生之前的关系。
换句话说,线程A
退出synchronized(x)
发生在线程B
进入synchronized(x)
之前。
类似地,写入volatile
字段x
发生在读取volatile
字段x
之前。
换句话说,关于可见性,synchronized
enter/exit对提供与volatile
读/写对完全相同的保证。
但是synchronized
对保证了可见性和原子性,而volatile
对只保证可见性。
Oops-忘记了一个例外:volatile long
和volatile double
确实保证了这些64位值的读写是原子的(即避免“字撕裂”)。
另一种看待它的方式是:在volatile
字段x
周围有一个小的synchronized(x')
,其中,x'
是与x
相对应的一些不可见锁对象(这不完全相同,因为对于volatile
,您必须将读取与写入配对,而所有同步的
关键字的工作方式相同)。
我觉得上述事实是不正确的,因为u、v和w可能存储在缓存和寄存器中,因为它们没有被定义为易失性。我这样说对吗?
这有点令人惊讶,但是synchronized
和volatile
提供的可见性保证适用于两个线程可见的所有内容,并且不仅限于被锁定的对象、volatile
字段本身、同一对象中的其他字段等。
这就是为什么如果你熟悉低级程序集/内核编程等,从内存障碍的角度考虑它们是有意义的。
1) 锁、同步变量和原子变量保证了读写原子性和可见性,并防止重新排序
2)易失性保证可见性和防止编译器和运行时重新排序
易失性字段的读写原子性有点棘手:对易失性字段的读写是原子性的,例如,如果在32位jvm上写入易失性长(64位),读写仍然是原子性的。你总是读完整的64位。但对易失性int或long的操作不是原子操作
读了很多关于易失性、原子性和可见性的文章后,有一个问题仍然存在。以下跨线程工作,当更新/读取“B”时,“A”始终可见: 原子变量是独立的对象,这同样适用吗?下面的操作会起作用吗? 如果答案是否定的,那么扩展AtomicInteger类并在其中包含“a”就可以了,因为AtomicInteger包装了一个volatile。
也许我对概念感到困惑,但是重写和在子类中创建一个新方法之间有什么区别呢?重写不就是在子类中创建一个不同于父类的新的特定方法吗?但这难道不是在子类中创建一个新方法所要做的吗?
问题内容: 当它适合使用原语(例如,或),而不是,或者,反之亦然? 问题答案: 可见性语义完全相同,当需要使用原子原语时,使用原子原语很有用。 例如: 可能在多线程环境中产生问题,因为变量可能会在两行之间变化。如果您需要测试和分配是原子的,则可以使用:
也许我对概念感到困惑,但是重写和在子类中创建一个新方法之间有什么区别呢?重写不就是在子类中创建一个不同于父类的新的特定方法吗?但这难道不是在子类中创建一个新方法所要做的吗?
我对下面的代码段有一个问题。结果可能有一个结果[0,1,0](这是用JCStress执行的测试)。那么这是怎么发生的呢?我认为数据写入(data=1)应该在Actor2(guard2=1)中写入到guard2之前执行。我说得对吗?我问,因为很多时候我读到挥发物周围的说明没有重新排序。此外,根据这一点:http://tutorials.jenkov.com/java-concurrency/vola
我试图比较两个主要Java实现的性能:Oracle和IBM运行以下测试: 通过以下方式运行上述代码: IBMJRE 1.8.0 java版本“1.8.0”java(TM)SE运行时环境(build pwa6480sr3fp22-20161213\U 02(SR3 FP22))IBM J9 VM(build 2.8,JRE 1.8.0 Windows 10 amd64-64压缩引用20161209\