Java和C#中的volatile语义背后的原因是什么?

yas*_*ash 12 c# java multithreading volatile multiprocessing

C#和Java都定义
*volatile读取具有获取语义
*volatile写入具有发布语义

我的问题是:

  1. 这是定义volatile的唯一正确方法吗?
  2. 如果没有,如果语义被颠倒,事情就会大不相同,也就是说
    • volatile读取具有释放语义
    • volatile写入具有获取语义

Kon*_*che 13

volatile语义背后的原因植根于Java内存模型,它是根据操作指定的:

  • 读取和写入变量
  • 锁定和解锁显示器
  • 开始和加入线程

Java内存模型为Java程序中可能发生的操作定义了一个名为happen-before部分排序.通常无法保证线程可以看到每个其他操作的结果.

比方说,你有两个动作一个.为了保证执行动作B的线程可以看到动作A的结果,A和B之间必须存在先发生关系.否则,JVM可以随意对它们进行重新排序.

未正确同步的程序可能会有数据争用.当一个变量被> 1个线程读取并由> = 1个线程写入时,会发生数据争用,但读取和写入操作不是通过发生前的排序来排序的.

因此,正确同步的程序没有数据争用,程序内的所有操作都按固定顺序发生.

所以行动通常只是部分订购,但也有一个订单:

  • 锁定获取和释放
  • 读取和写入volatile变量

这些行动是完全有序的.

这使得在"后续"锁定获取和易失性变量读取方面描述发生之前是明智的.

关于你的问题:

  1. 通过之前发生的关系,您可以选择另一种定义 volatile
  2. 颠倒顺序对上面的定义没有意义,特别是因为涉及总订单.

之前发生

这说明了当两个线程使用公共同步时发生在之前的关系.线程A中的所有操作都按程序顺序规则排序,线程B中的操作也是如此.因为A释放锁定M而B 随后获取M,因此在释放锁定之前A中的所有操作都在B中的操作之前排序获得锁定后.当两个线程在不同的锁上同步时,我们不能说它们之间的动作顺序,在两个线程中的动作之间没有发生 - 之前的关系.

来源:实践中的Java并发


Ian*_*rts 5

获取/释放语义的强大之处不在于其他线程多久能够看到易失性字段本身的新写入值,而在于易失性操作在不同线程之间建立先发生关系的方式.如果线程A读取volatile字段并且看到在另一个线程B中写入该字段的值,那么线程A也保证在线程B执行之前看到线程B 写入其他(不一定是易失性)变量的值.易失写.这看起来像缓存刷新,但仅从读取volatile的线程的角度来看,其他不接触volatile字段的线程没有关于B的排序保证,并且可能会看到一些早期的非易失性写入但是如果编译器/ JIT如此倾向,则不是其他人.

监视器获取/释放的特征类似于它们引发的先发生关系 - 在监视器释放之前,一个线程的动作保证在另一个线程后续获取同一监视器之后可见.Volatiles为您提供与监视器同步相同的排序保证,但不会阻塞.

  • [本文](http://www.ibm.com/developerworks/library/j-jtp03304/)提供了很好的解释.基本上它使挥发性更有用,因为它们实际上适用于设置易失性标志以表示特定操作已经完成的情况 - 另一个读取标志的线程可以知道操作_is_完成而不是看到某些部分完成中间状态. (2认同)