我想从 Java Concurrency in Practice 中理解这一行 -
超薄空中安全是一种安全保证,当线程在没有同步的情况下读取变量时,它可能会看到一个陈旧的值,但该值将是一个线程设置的值,而不是一些随机值。
价值是随机的意味着什么?怎么会进入那个状态?伪代码中的一个例子非常有用。
答案 0 :(得分:2)
我能想到的最好的例子是在32位CPU中写long
。
根据Java标准,long
必须是64位宽。 32位CPU无法在一次原子写入中写入long
值,它必须写入两个32位整数才能实现此目的。
假设一个线程只写了第一个32位值,那么,OS在同一个核心上调度另一个(Java)线程,尝试读取该半更新值 - 它将看到随机值。你给出的报价基本上说“不可能发生”。
答案 1 :(得分:1)
在Java中,变量在创建时使用“已知”值进行初始化(在int value = 5;
情况下,默认值为“零样”值或提供的值)。
默认初始化在spec的第4.12.5节中描述。
这就是说,变量初始化为“零”(对于该类型的解释)。
现在,通常,变量的赋值是原子的。也就是说,它们“立刻”发生,变量的值从一个值变为新值。
对于long
和double
类型的变量,可能会出现例外情况。这在spec中有所描述。
实际上,这些值是64位宽,可以分两步设置,每个32位一半。
总之,上述要求意味着变量的值可能处于以下状态之一:
相当确定性,并且可以说,没有任何价值可以通过空气来阅读。
这与C或C ++等语言不同,后者并不总是初始化变量。因此,您可以读取一些随机数据(以前在该内存位置的任何内容)。
答案 2 :(得分:1)
现有的两个答案都描述了读写操作的 ,在我看来,这与空洞的值不太一样。 / p>
撕裂基本上是指计算机重写
int g = 0x1234;
void threadA() {
g = 0xABCD;
}
void threadB() {
int local = g;
}
进入
int g = 0x1234;
void threadA() {
g = 0xAB00; // "tearing"
g += 0x00CD;
}
void threadB() {
int local = g; // may read 0xAB00, which threadA never stored
}
(根据指令集,您可能会想像看到0xAB34、0x12CD,0x00CD,0x00AB等)
但是,凭空传播(根据我的有限经验)通常意味着编译器会重写
int g1 = 0x1234;
bool g2 = true;
void threadA() {
g1 = (g2 ? 0xABCD : 0x5678); // since g2 is true, this stores 0xABCD
}
void threadB() {
int local = g1;
}
进入
int g1 = 0x1234;
bool g2 = false;
void threadA() {
g1 = 0x5678;
if (g2) g1 = 0xABCD;
}
void threadB() {
int local = g1; // may read 0x5678, which threadA never stored
}
这与“推测执行”(我认为更多是硬件术语)密切相关,并且不取决于读取或写入的中断。
另请参阅我对这个重复问题(https://stackoverflow.com/a/26500593/1424877)的回答。