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)
编辑 - 删除"请评论"评论.
“无中生有的安全性”确实比“顺序一致性”要弱得多,但从某种意义上说,人们可能会称其为“强”。也就是说,如果您的系统提供了大田安全,你可以推理你的程序多比,如果你的系统中没有提供的更好。如果您使用的系统不提供 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=42
和y=17
即使他们都没有开始时那样!
我知道人们通常依靠时间旅行悖论的比喻来描述值 42 和 17 是如何“凭空”出现在主内存中的,但我认为有线新闻的比喻更准确。;)
这种安全性是否很弱,因为它可能包含陈旧价值?
是."Java Concurrency in Practice"中的引用试图指出你number
可能是0
或者42
取决于访问非同步字段所固有的竞争条件,但它不会(让我们说)1
- 价值不会"超出" -稀薄的空气".它可能是陈旧的,对于对象,甚至可能是long
64位值,具体取决于您的硬件架构,也可能会部分更新,但它不会有一些随机值.
在您的示例中number
,初始化0
为主线程然后由主线程设置42
为number
内部的可能值ReaderThread
.
编辑:
正如Voo和yshavit指出的那样,JLS第17.7节特别提到有一些体系结构将64位操作实现为可以中断的2个独立的32位操作.这意味着一个线程可能只看到另一半线程对字段的更新.虽然不是"凭空",但由于按位数表示,结果值似乎是任何线程都没有设置的值.