挥发性背负。这足以提高可视性吗?

这是关于不稳定的背负。目的:我想达到轻量级的 vars 可见性。a_b_c的一致性并不重要。我有一堆vars,我不想让它们都不稳定。

此代码线程是否安全?

class A {
    public int a, b, c;
    volatile int sync;

    public void setup() {
        a = 2;
        b = 3;
        c = 4;
    }

    public void sync() {
        sync++;
    }
}

final static A aaa = new A();

Thread0:
aaa.setup();
end

Thread1:
for(;;) {aaa.sync(); logic with aaa.a, aaa.b, aaa.c}

Thread2:
for(;;) {aaa.sync(); logic with aaa.a, aaa.b, aaa.c}

答案 1

Java 内存模型定义了“发生前”关系,该关系具有以下属性(以及其他属性):

  • “线程中的每个操作都发生在该线程中以程序顺序稍后出现的每个操作之前”(程序顺序规则)
  • “写入易失性字段发生在每次后续读取同一易失性之前”(易失性变量规则)

这两个属性以及发生前关系的传递性意味着OP以以下方式寻求的可见性保证:

  1. 线程 1 中的写入发生在线程 1 中写入 到 的调用(程序顺序规则)之前asyncsync()
  2. 在对线程 1 的调用中写入之前 - 在对线程 2 中的调用中读取到之前(易失性变量规则)。syncsync()syncsync
  3. 从调用中读取到线程 2 中的读取发生在线程 2 中的读取之前(程序顺序规则)。syncsync()a

这意味着问题的答案是肯定的,即在线程 1 和 2 的每次迭代中调用 to 可确保对 和其他线程的更改的可见性。请注意,这仅确保可见性。不存在互斥保证,因此所有不变物都具有约束力,并且可能被违反。sync()abcabc

另请参阅 Java 理论和实践:修复 Java 内存模型,第 2 部分。特别是“不稳定的新保证”部分,其中说

在新的内存模型下,当线程 A 写入易失性变量 V,而线程 B 从 V 读取时,在写入 V 时对 A 可见的任何变量值现在都保证对 B 可见。


答案 2

在线程之间递增值从来都不是线程安全的,只有 。这只能确保每个线程获得一个最新的值,而不是增量是原子的,因为在汇编程序级别,您的++实际上是可以交错的几条指令。volatile

您应该用于快速原子增量。AtomicInteger

编辑:再次阅读你需要的实际上是一个记忆围栏。Java没有内存栅栏指令,但你可以使用锁来对内存栅栏“副作用”。在这种情况下,声明同步同步方法以引入隐式围栏:

void synchronized sync() {
    sync++;
}