出于稀薄的空气安全

Kev*_*ith 2 java multithreading

Java Concurrency in Practice解释了这个概念:

当一个线程在没有同步的情况下读取一个变量时,它可能会看到一个陈旧的值,但至少它会看到某个线程实际放置的值而不是某个随机值.这种安全保证称为超薄空气安全.

这种安全性是否很弱,因为它可能包含陈旧价值?

也许这个片段,at least it sees a value that was actually placed there by some thread than some random value是因为本书的上一个主题是JVM重新命令变量语句的可能性sharing variables without synchronization

示例:根据重新排序:42或0可以打印出来.

public class NoVisibility {
    private static boolean ready;
    private static int number;

    private static class ReaderThread extends Thread {
        public void run() {
            while(!ready)  
                Thread.yield();
            System.out.println(number);
            }
        }

    public static void main(String[] args) {
        new ReaderThread().start();
        number = 42;
        ready = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑 - 删除"请评论"评论.

Quu*_*one 7

“无中生有的安全性”确实比“顺序一致性”要弱得多,但从某种意义上说,人们可能会称其为“强”。也就是说,如果您的系统提供了大田安全,你可以推理你的程序比,如果你的系统中没有提供的更好。如果您使用的系统不提供 OOTA 安全性,那么您基本上就完蛋了。

Hans Boehm 和 Brian Demsky 最近写了一篇关于该主题的论文,标题为“取缔鬼:避免凭空出现的结果” (PDF 可在 ACM.org 上获得)。他们给出了一些非常好的例子来说明非 OOTA 安全系统如何工作,以及在这样的系统中可能会出现什么样的错误。

基本思想是允许某些系统进行推测执行,包括对内存(缓存)的推测存储,如果系统的推测结果不正确,则稍后可以撤消。这在单线程系统中效果很好,但在多线程系统中,两个线程的推测可能会相互影响,从而造成“失控的谣言工厂”:处理器 A 认为 x=42,因为处理器 B 推测存储了 42在那里,但处理器 B 仅存储 x=42,因为处理器 A 告诉它 y=17……但处理器 A 只是基于 x=42 的假设进行推测!

void rivera() {                 void lemon() {
    if (x == 42) {                  if (y == 17) {
        y = 17;                         x = 42;
    }                               }
}                               }
Run Code Online (Sandbox Code Playgroud)

“非 OOTA 安全”系统可能会重写这些方法,使其看起来像(使用伪 Python-Javascript 语法,抱歉)

void rivera() {
    assert(y not in cache);  // for the sake of argument
    cache.y = 17;  // for the sake of speed, speculatively report that y=17
    if (x not in cache) {
        cache.x = x;  // meanwhile, slowly fetch the real value of x
    }
    if (cache.x != 42) {
        delete cache.y;  // discard the earlier, speculative write
    }
    // and then later write cache.y back into the y in main memory
}
Run Code Online (Sandbox Code Playgroud)

您可以看到如果lemon()trustrivera()的推测性报告,这将是一个巨大的问题,cache.y = 17反之亦然。我们可能最终,这两种方法都完成后,与形势x=42y=17即使他们都没有开始时那样!

我知道人们通常依靠时间旅行悖论的比喻来描述值 42 和 17 是如何“凭空”出现在主内存中的,但我认为有线新闻的比喻更准确。;)


Gra*_*ray 6

这种安全性是否很弱,因为它可能包含陈旧价值?

是."Java Concurrency in Practice"中的引用试图指出你number可能是0或者42取决于访问非同步字段所固有的竞争条件,但它不会(让我们说)1- 价值不会"超出" -稀薄的空气".它可能是陈旧的,对于对象,甚至可能是long64位值,具体取决于您的硬件架构,也可能会部分更新,但它不会有一些随机值.

在您的示例中number,初始化0为主线程然后由主线程设置42number内部的可能值ReaderThread.

编辑:

正如Voo和yshavit指出的那样,JLS第17.7节特别提到有一些体系结构将64位操作实现为可以中断的2个独立的32位操作.这意味着一个线程可能只看到另一半线程对字段的更新.虽然不是"凭空",但由于按位数表示,结果值似乎是任何线程都没有设置的值.

  • 同意的人.我添加了我的答案.谢谢. (2认同)