这个问题仅涉及内存可见性,而不是发生在之前和之后。Java中有四种方法可以保证一个线程中对内存的更改对另一个线程可见。(参考http://gee.cs.oswego.edu/dl/cpj/jmm.html)
- 写入线程释放同步锁,读取线程随后获取相同的同步锁。
- 如果一个字段被声明为易失性,则写入它的任何值都会在写入线程执行任何进一步的内存操作之前被写入线程刷新并使其可见(即,出于手头的目的,它会立即刷新)。
- 线程第一次访问对象的字段时,它会看到该字段的初始值或自其他线程写入以来的值。
- 当线程终止时,所有写入的变量都被刷新到主内存。
根据Java Concurrency in Practice,有关此类问题的圣经:
volatile 变量的可见性影响超出了 volatile 变量本身的值。当线程A写入 volatile 变量并且随后线程B读取相同的变量时,在写入 volatile 变量之前对A可见的所有变量的值在读取 volatile 变量后对B可见。
不稳定的问题
这是否意味着 JVM 实际上会跟踪 volatile 变量的读取和写入,以便知道如何将内存从A刷新到B而不是A到C?所以A写入变量,然后C从变量中读取,然后B从变量中读取,刷新是在A和B以及A和C之间按线程完成的,但不是 B和C?或者,这是否意味着所有缓存的内存都被刷新,而不管线程如何?是仅刷新 volatile 变量,还是所有缓存内存?
同步问题
对于synchronized
关键字flushing,它表示只有在锁内更新的内存才能保证发布到其他线程。这意味着在下面的代码中,两个线程运行method()
,离开同步块将刷新staticVar2
到另一个线程,但不是 staticVar1
,对吗?
此外,在 中method2()
,如果另一个线程正在执行,则同步结束differentLock
可能会导致发生前发生后发生问题method()
。但是,问题在于可见性。如果线程A执行method
,然后线程B稍后执行,即使两个线程没有在同一个锁上同步,method2()
值是staticVar2
从A发布到B吗?
static int staticVar1, staticVar2;
void method() {
staticVar1++;
synchronized (lock) {
staticVar2++;
}
}
void method2() {
synchronized (differentLock) {
staticVar2++;
}
}
静态问题
在我看来,如果staticVar1
从未更新到其他线程,那么任何程序中的所有静态变量都需要volatile
声明,或者只能在synchronized
块中访问。这似乎相当苛刻,但它是正确的吗?我确实在我的时间里看到了很多不同步的静态变量。
总之
- 易失性读写是否将所有内存刷新到所有线程,还是仅在两个访问线程之间?无论答案是什么,是所有内存都被刷新还是只有 volatile 变量?
- 退出同步块时是否会刷新所有更改的内存,还是仅刷新块内更改的内存?如果不是所有内存都被刷新,线程同步的锁对象是否必须相同才能看到值(即锁对象对内存可见性有任何影响)?
- 两个线程访问的所有静态变量都必须同步吗?