Java内存模型保证易失性

时间:2020-03-22 15:46:23

标签: java volatile

我对易变语义毫无疑问。
假设有三个线程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中看到一个值(至少与此保持一致)值)。

但是,这些陈述是正确的吗?

  1. Java内存模型保证T1在第11点看到的值至少与T3在第5点看到的值一样(来自T3的本地内存或更多实际值,但即使有一个共享内存中有更多实际值,那么T1可能看不到它。)
  2. 根本无法保证T1在点12处看到的东西,特别是不能保证它在点3处看到值设置。此外,如果在任何线程中在点1之前对x进行写操作,则在点T1 12可以看到一些过时的价值。 如果在点7的T3的本地内存中有x的值,JMM保证T1在点12可以看到x,但是假设在点7之前在T3中没有对x进行写/读操作,则存在没有这样的保证。
  3. 在点8和点9之间没有发生事前关系,因为它们是不同的易变变量。如果JVM以某种方式实现Java内存模型,则它会在易失变量的读操作中将本地内存刷新到共享内存,并在对易失变量的写操作时使本地内存无效,作为article中的状态同步语义,那么,副作用是,第8点和第9点之间会发生事前关系,但是Java语言规范中并未对此进行严格定义。
  4. 如果在点7和8处有读取动作而不是写动作,则点10处的T1仍然会看到值30,因为事前发生关系不仅适用于读写易失序列,而且适用于读写,易写序列也可以读写。

请确认我的理解是正确的。

1 个答案:

答案 0 :(得分:1)

只要您的获取/设置操作仅获取和设置变量,那么所有假设都是正确的。

在Java中,变量存储在内存中。但是编译器(和运行时)将允许将变量临时存储在CPU高速缓存中,以便在算法或代码段期间可以更快地进行读写。

这种缓存的缺点是当Core完成变量时,它将把它写回内存,就好像它只被更新过一次一样。使用其他变量时,其他核心无法看到其状态。更糟糕的是,无法保证何时将其写回到内存。

通过将变量设置为Volatile,您告诉Java不允许将该变量放入ANY缓存。对变量的读或写必须在内存中进行。

这意味着Volatile将对原子变量进行单个操作。但是,这也会使对变量的长时间运算变得更慢。因此,volatile并不是提高多线程代码性能的解决方案。

值得注意的是,需要多次读或写的操作是 not 原子的。例如,实际上为i = i + 1的i ++可以在写入完成之前更改i的值。

如果您需要保证操作是原子发生的,则可以使用锁或semtex(慢速),也可以使用诸如写入时复制(COW)之类的聪明范例来允许原子读取和原子写入。

相关问题