java—可变变量写入之后写入的变量的可见性

y53ybaqx  于 2021-06-29  发布在  Java
关注(0)|答案(3)|浏览(356)
public class Test {
    private static volatile boolean flag = false;
    private static int i = 1;
    public static void main(String[] args) {
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            flag = true;
            i += 1;
        }).start();
        new Thread(() -> {
            while (!flag) {
                if (i != 1) {
                    System.out.println(i);
                }
            }
            System.out.println(flag);
            System.out.println(i);
        }).start();
    }
}

变量 i 写在volatile变量标志之后,但代码输出true 2。看来 i 第一个线程对第二个线程可见。
根据我的理解,变量我应该先写flag,然后第二个线程才能知道变化。

noj0wjuj

noj0wjuj1#

内存模型定义了保证,但是在保证之上可能发生任何事情。
在x86上,所有的写操作都有发布语义,一旦您写入一个变量,它的更新值将尽快从其他线程中可见。
因此,写入volatile变量之前的操作发生在读取volatile变量之后的操作之前,这一事实并不妨碍写入volatile变量之后的操作在读取volatile变量之后变得可见。

pu82cl6c

pu82cl6c2#

你的代码遭遇数据竞争。
数据争用是指对同一地址有两个内存操作,它们不是按“发生在”关系排序的,并且这些操作中至少有一个是写操作。
在这种情况下,写入 i 这就是问题所在。
写信给 i ,是在写入volatile变量之后 flag 因此,写作和写作之间不存在“先发生后发生”的关系 i 和阅读 i .
如果你愿意写 i 在你写信给 flag ,关系之前会发生以下情况:
i 发生在写之前 flag 由于程序顺序规则
flag 在读到之前发生 flag 由于易失性变量规则(在硬件级别上,这是缓存一致性的任务)。
阅读 flag 发生在 i 由于程序顺序规则。
因为before关系是传递的,所以 i 在读取if之前发生 i .
就像你已经指出的,如果你移动 i 前面写着国旗;数据竞赛已经结束。

s4chpxco

s4chpxco3#

根据语言标准(§17.4):
字段可以声明为volatile,在这种情况下,java内存模型确保所有线程都能看到变量的一致值
因此,非正式地说,所有线程都可以看到该变量的must update值。
然而,volatile子句不仅意味着确保目标变量的可见性保证,而且还意味着完全的volatile可见性保证,即:
实际上,javavolatile的可见性保证超出了volatile变量本身。能见度保证如下:
如果线程a写入一个易失性变量,而线程b随后读取同一个易失性变量,那么线程a在写入该易失性变量之前可见的所有变量在线程b读取该易失性变量之后也将可见。
如果线程a读取一个易失性变量,那么在读取该易失性变量时线程a可见的所有变量也将从主内存中重新读取。
根据我的理解,变量我应该先写flag,然后第二个线程才能知道变化。
“在写入volatile变量之前线程a可以看到的所有变量”,它并不指对这些变量的操作。

相关问题