使用volatile和synchronized时,刷新或发布到各种线程的内存范围是多少?

这个问题仅涉及内存可见性,不会发生在之前和发生之后。 Java中有四种方法可以保证一个线程中的内存更改对另一个线程可见。 (参考http://gee.cs.oswego.edu/dl/cpj/jmm.html )

  1. 写入线程释放同步锁,读取线程随后获取相同的同步锁。
  2. 如果一个字段被声明为volatile,则在写入器线程执行任何进一步的内存操作之前,写入其中的任何值都会被写入器线程刷新并使其可见(即,出于手头的目的,它立即被刷新)。
  3. 线程第一次访问对象的字段时,它会看到字段的初始值或自某个其他线程写入的值。
  4. 当一个线程终止时,所有写入的变量都被刷新到主存储器。

根据Java Concurrency in Practice,关于这些问题的圣经:

volatile变量的可见性效果超出了volatile变量本身的值。 当线程A写入易失性变量并且随后线程B读取相同的变量时,在读取volatile变量之后,在写入易失性变量之前A可见的所有变量的值变为可见。

挥发性的问题

这是否意味着JVM实际上跟踪了易失性变量读写,以便知道如何将内存从A刷新到B而不是AC ? 所以A写入变量,后来C从变量读取,然后B从变量读取,刷新是在AB以及AC之间的每个线程基础上完成的,而不是 BC ? 或者,它是否暗示所有缓存的内存都被刷新,无论线程如何? 只是刷新了volatile变量,还是所有缓存的内存?

同步问题

对于synchronized关键字刷新,它表示只有锁内部更新的内存才能保证发布到其他线程。 这意味着在下面的代码中,两个运行method()线程,保留synchronized块会将staticVar2刷新到另一个线程,但不是 staticVar1 ,这是正确的吗?

此外,在method2() ,如果另一个线程正在执行method()则可以导致在发生问题之前发生 – 遇到问题。 但是,问题在于可见性。 如果线程A执行method ,那么后来的线程B执行method2() ,是从AB发布的staticVar2的值,即使这两个线程没有通过同一个锁同步?

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

静态问题

在我看来,如果staticVar1永远不会更新到其他线程,那么任何程序中的所有静态变量都需要一个volatile声明,或者只能在synchronized块中访问。 这似乎相当苛刻,但它是否正确? 我确定在我的时间里看到了许多不同步的静态变量。

综上所述

  1. 易失性读写会将所有内存刷新到所有线程,还是仅在两个访问线程之间? 无论哪个答案,是所有内存都刷新了还是只有易变量?
  2. 退出同步块时,是否所有更改的内存都被刷新,或者只是块中更改的内存? 如果没有刷新所有内存,则线程同步的锁定对象是否必须相同才能看到该值(即锁定对象是否对内存可见性有影响)?
  3. 是否必须同步两个线程访问的所有静态变量?

内存方面没有范围限制。 当您具有读或写屏障时,它适用于所有内存读/写。

我在哪里看到了内存映射的限制。 当你的内存映射文件时,你必须要小心如何使这个可用于另一个线程,因为这个新的内存映射可能在另一个线程中不可见,立即导致BUS错误(以及JVM崩溃)这似乎是一个操作系统错误,因为最新版本的Linux和Windows似乎没有这个问题。

这意味着在下面的代码中,两个运行method()的线程,保留synchronized块会将staticVar2刷新到另一个线程,但不是staticVar1,这是正确的吗?

当staticVar2可能更快时,statixVar1将始终被刷新。 不保证何时,但订单有保证。

如果线程A执行方法,那么后来的线程B执行method2(),是从A到B发布的staticVar2的值,即使这两个线程没有通过同一个锁同步?

是的,使用的锁对于先发生的保证无关紧要。

易失性读写会将所有内存刷新到所有线程,还是仅在两个访问线程之间? 无论哪个答案,是所有内存都刷新了还是只有易变量?

所有脏内存都在写屏障上刷新,所有读取在读屏障上都是顺序一致的。 volatile执行写操作时的写屏障和读操作时的读屏障。

退出同步块时,是否所有更改的内存都被刷新,或者只是块中更改的内存?

该线程改变了所有内存。

是否必须同步两个线程访问的所有静态变量?

仅当一个线程修改变量时。 任何数量的线程都可以读取静态值而无需同步。