什么是超薄空中安全?

时间:2017-03-03 20:40:05

标签: java multithreading concurrency

我想从 Java Concurrency in Practice 中理解这一行 -

  

超薄空中安全是一种安全保证,当线程在没有同步的情况下读取变量时,它可能会看到一个陈旧的值,但该值将是一个线程设置的值,而不是一些随机值。

价值是随机的意味着什么?怎么会进入那个状态?伪代码中的一个例子非常有用。

3 个答案:

答案 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节中描述。​​

这就是说,变量初始化为“零”(对于该类型的解释)。

现在,通常,变量的赋值是原子的。也就是说,它们“立刻”发生,变量的值从一个值变为新值。

对于longdouble类型的变量,可能会出现例外情况。这在spec中有所描述。 实际上,这些值是64位宽,可以分两步设置,每个32位一半。

总之,上述要求意味着变量的值可能处于以下状态之一:

  1. 默认值(“零”/ null)
  2. 线程设置的值
  3. 用于非挥发性长或双倍,所需值的一半(0 |半或半| 0)
  4. 相当确定性,并且可以说,没有任何价值可以通过空气来阅读。

    这与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)的回答。