这是关于不稳定的背驮式。 目的:我希望达到轻量级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}
答案 0 :(得分:32)
Java内存模型定义发生在之前关系,该关系具有以下属性(以及其他):
这两个属性以及发生在关系之前的传递性意味着可见性保证OP以下列方式寻求:
a
的写入发生在在线程1(程序顺序规则)中对sync
的调用中sync()
的写入之前。 sync
的调用中对sync()
的写入发生在<{1>}调用sync
之前sync
在线程2(易变变量规则)。sync
的调用中sync()
的读取发生在从线程2中的a
读取之前(程序顺序规则)。 这意味着问题的答案是肯定的,即在线程1和2中每次迭代中调用sync()
可确保对a
,b
和{{ 1}}到其他线程。请注意,这仅确保可见性。不存在互斥保证,因此可能违反绑定c
,a
和b
的所有不变量。
另见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
。