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;
}
}
已编辑 - 已删除“请评论”评论。
答案 0 :(得分:6)
这种安全性是否很弱,因为它可能包含陈旧价值?
是。 “Java Concurrency in Practice”中的引用试图指出您的number
可能是0
或42
,具体取决于访问非同步字段所固有的竞争条件,但它不会(让我们说)1
- 价值不会“超薄”。它可能是陈旧的,对于对象,甚至可能long
64位值,具体取决于您的硬件架构,也可能部分更新,但它不会有一些随机值。< / p>
在您的示例中,number
已初始化为0
,然后由主线程设置为42
,因此number
中ReaderThread
的可能值为{{1}} }}
修改强>
正如Voo和yshavit指出的那样,JLS section 17.7 specifically提到有一些架构将64位操作实现为可以中断的2个独立的32位操作。这意味着一个线程可能只看到另一半线程对字段的更新。虽然不是“凭空”,但由于按位数字表示,结果值似乎是未被任何线程设置的值。
答案 1 :(得分:3)
“超薄航空安全”确实是一个比例如“顺序一致性”,但在某种意义上,人们可能称之为“强大”。也就是说,如果您的系统提供OOTA安全性,您可以比您的系统没有提供更好的程序多。如果您使用的系统不提供OOTA安全性,那么您基本上就是搞砸了。
Hans Boehm和Brian Demsky最近撰写了一篇题为“禁止鬼魂:避免稀薄的结果”的论文(PDF available on 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;
} }
} }
“非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
}
如果lemon()
信任rivera()
的投机报告cache.y = 17
,您可以看到这将是一个巨大的问题,反之亦然。在两种方法完成后,我们最终会遇到x=42
和y=17
的情况,即使他们都没有这样开始!
我知道人们通常依靠time-travel paradox隐喻来描述价值42和17如何“凭空捏造”在主存中,但我认为有线新闻比喻更为准确。 ;)