我对volatile语义有一些疑问。
假设有三个线程T1、T2和T3以及给定类的一个实例。
假设有三个线程T1、T2和T3以及给定类的一个实例。
class Foo {
private int x = 1;
private int y = 2;
private int z = 3;
private volatile int w = 4;
private volatile int v = 5;
public void setX(int x) {
this.x = x;
}
public int getX() {
return this.x;
}
(...)
}
假设发生了以下读/写操作序列:
1. (T1) foo.getX(); // stored in a local memory of T1
2. (T1) foo.getY(); // stored in a local memory of T1
3. (T2) foo.setX(10);
4. (T2) foo.setY(20);
5. (T3) foo.getY(); // T3 may see 2 or 20, no guarantees if a write action from point 4 is visible to T3
6. (T3) foo.setZ(30);
7. (T3) foo.setW(40);
8. (T3) foo.setV(50);
9. (T1) foo.getW()
10. (T1) foo.getZ()
11. (T1) foo.getY()
12. (T1) foo.getX()
我知道在第9个点,T1将看到在第7个点设置的值,并且在第10个点,T1将看到在第6个点设置的值(确切地说,至少与此值一样最新)。
但是,这些陈述是否正确?
- Java内存模型保证T1在第11点至少能看到一个与T3在第5点看到的值同步的值(来自于T3的本地内存或者更加实际的值,但即使共享内存中有一个更加实际的值,它可能对T1不可见)。
- 在第12点,T1所看到的值没有任何保证,特别是没有保证它能看到在第3点设置的值。此外,如果在线程中的任何一个点之前对x进行了任何写操作,T1在第12点可能会看到一些旧的值。如果在第7点时,T3的本地内存中存在任何x的值,则JMM保证T1在第12点将看到该值,但是假设在第7点之前的T3中没有对x进行任何写/读操作,则没有这样的保证。
- 第8点和第9点之间不存在happens-before关系,因为它们是不同的volatile变量。如果JVM以一种方式实现Java内存模型,在读取volatile变量时将本地内存刷新到共享内存,并在写入volatile变量时使本地内存无效,就像article中描述的synchronized语义一样,那么副作用将是第8点和第9点之间存在happens-before关系,但它在Java语言规范中没有严格定义。
- 如果在第7点和第8点有读操作而不是写操作,T1在第10点仍将看到值30,因为happens-before关系不仅适用于写-读volatile序列,还适用于读-读、写-写和读-写volatile序列。