所以我认为我已经足够了解这些东西,直到我读到让我怀疑这个主题的东西。我几乎可以肯定这本书是不正确的,但也想问社区。 p> PS:还没有看过这本书的勘误,所以很容易被公开为错误。
简化示例:
public class VolatileMain {
private volatile int a = 0;
private String text = "";
public static void main(String[] args) throws Exception {
VolatileMain vm = new VolatileMain();
Thread writer = new Thread() {
@Override
public void run() {
System.out.println("Running thread " + Thread.currentThread().getName());
vm.text = "hello world";
vm.a = 5;
}
};
writer.start();
writer.join();
System.out.println("Running thread " + Thread.currentThread().getName());
System.out.println(vm.a);
System.out.println(vm.text);
}
}
所以给出这个例子,假设写入"文本"是正确的。通过线程编写器保证可以被任何其他读取它的线程看到吗?
似乎作者正在依赖于变量" a"的变体语义。并确保写入"文本"当" a"满脸通红,这是保证吗?
我认为不是,但我自己的快速测试(上图)恰恰相反
你的想法。
答案 0 :(得分:4)
假设线程编写器对“text”的写入保证可以被读取它的任何其他线程看到是正确的吗?
没有。但是,保证在阅读a
之前读取text
的任何其他线程都可以看到它,如您的示例所示:
text
的写入发生在编写器线程中写入a
之前a
的写入发生在主线程中读取a
之前text
的写入发生在阅读a
之前。答案 1 :(得分:4)
不,它不能保证,因为"冲洗"并不那么简单。即使您实际上将非易失性内容写入"主内存",也不能保证其他线程中的后续读取将从该主内存中读取它。请考虑以下示例:
public class VolatileMain {
private volatile int a = 0;
private String text = "";
public static void main(String[] args) throws Exception {
VolatileMain vm = new VolatileMain();
Thread writer = new Thread() {
@Override
public void run() {
// Added sleep here, so waitForText method has chance to JIT-compile
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
System.out.println("Running thread " + Thread.currentThread().getName());
vm.text = "hello world";
vm.a = 5;
System.out.println("Text changed!");
}
};
writer.start();
waitForText(vm);
writer.join();
System.out.println("Running thread " + Thread.currentThread().getName());
System.out.println(vm.a);
System.out.println(vm.text);
}
// Wait for text change in the spin-loop
private static void waitForText(VolatileMain vm) {
int i = 0;
/*
@Edit by Soner
Compiler may do following steps to optimize in lieu.
String myCache = vm.text;
-- Assume that here myCache is "" -- so stay forever.
while (myCache.equals("")) { i++; }
*/
while (vm.text.equals("")) {
i++;
}
System.out.println("Wait complete: " + i);
}
}
很有可能waitForText
永远不会完成,只是因为JIT编译器会优化它并将vm.text
的读数移出循环(因为它不易变形) ,循环中不执行易失性读取,并且text
永远不会在循环内部发生变化)使循环无限。
易失性读/写不仅会影响内存承诺,还会改变JIT编译策略。在while循环中添加vm.a
的读数,程序将正常工作。