挥发性背驮式。这是否足以提高能见度?

时间:2012-01-07 12:07:46

标签: java synchronization volatile

这是关于不稳定的背驮式。 目的:我希望达到轻量级vars visibilty。 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}

5 个答案:

答案 0 :(得分:32)

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

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

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

  1. 线程1 中对a的写入发生在在线程1(程序顺序规则)中对sync的调用中sync()的写入之前。
  2. 在线程1 中对sync的调用中对sync()的写入发生在<{1>}调用sync之前sync在线程2(易变变量规则)。
  3. 在线程2 中对sync的调用中sync()的读取发生在从线程2中的a读取之前(程序顺序规则)。
  4. 这意味着问题的答案是肯定的,即在线程1和2中每次迭代中调用sync()可确保对ab和{{ 1}}到其他线程。请注意,这仅确保可见性。不存在互斥保证,因此可能违反绑定cab的所有不变量。

    另见Java theory and practice: Fixing the Java Memory Model, Part 2。特别是“挥发性新保证”部分,其中说

      

    在新内存模型下,线程A写入volatile时   变量V,线程B从V读取任何变量值   现在可以保证在写入V时对A可见   B可见。

答案 1 :(得分:4)

仅使用volatile增加线程之间的值绝不是线程安全的。这只能确保每个线程获得最新值,而不是增量是原子的,因为在汇编程序级别,++实际上是几个可以交错的指令。

您应该使用AtomicInteger进行快速原子增量。

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

void synchronized sync() {
    sync++;
}

答案 2 :(得分:1)

来自javadoc:

  

监视器的解锁(同步块或方法退出)   发生在每个后续锁定之前(同步块或方法)   那个监视器的入口)。而且因为之前发生的关系   是传递的,解锁之前线程的所有动作   发生 - 在任何线程锁定之后的所有操作之前发生   监视。

     

在每次后续读取之前发生对易失性字段的写入   那个领域。易失性字段的写入和读取具有相似之处   进入和退出监视器时的内存一致性效果,但确实如此   不需要互斥锁定。

所以我认为在这种情况下写入volatile var并不等同于同步,并且它不保证在Thread1到Thread2的更改顺序和可见性之前发生

答案 3 :(得分:1)

模式通常是这样的。

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

但是,虽然这可以保证其他线程会看到此更改,但其他线程可以看到不完整的更改。例如Thread2可能会看到a = 2,b = 3,c = 0.或甚至可能a = 2,b = 0,c = 4;

在阅读器上使用sync()无济于事。

答案 4 :(得分:-1)

您根本不需要手动同步,只需使用自动同步的数据结构,例如java.util.concurrent.atomic.AtomicInteger

您也可以制作sync()方法synchronized