volatile
原来的语义只保证 volatile
字段的读写直接在主存而不是寄存器或者本地处理器缓存中进行,并且代表线程对 volatile 变量进行的这些操作是按线程要求的顺序进行的。换句话说,这意味着老的内存模型只保证正在读或写的变量的可见性,不保证写入其他变量的可见性。虽然可以容易实现它,但是它没有像最初设想的那么有用。
虽然对 volatile 变量的读和写不能与对其他 volatile 变量的读和写一起重新排序,但是它们仍然可以与对 nonvolatile 变量的读写一起重新排序。在 第 1 部分 中,介绍了清单 1 的代码(在旧的内存模型中)是如何不足以保证线程 B 看到 configOptions
及通过 configOptions
间接可及的所有变量(如 Map
元素)的正确值,因为 configOptions
的初始化可能已经随 volatile initialized
变量进行重新排序。
Map configOptions; char[] configText; volatile boolean initialized = false; // In Thread A configOptions = new HashMap(); configText = readConfigFile(fileName); processConfigOptions(configText, configOptions); initialized = true; // In Thread B while (!initialized) sleep(); // use configOptions |
不幸地,这是 volatile 常见用例——用一个 volatile 字段作为“守护”表明已经初始化了一组共享变量。JSR 133 Expert Group 决定让 volatile 读写不能与其他内存操作一起重新排序是有意义的——可以准确地支持这种和其他类似的用例。在新的内存模型下,如果当线程 A 写入 volatile 变量 V 而线程 B 读取 V 时,那么在写入 V 时,A 可见的所有变量值现在都可以保证对 B 是可见的。结果就是作用更大的 volatile
语义,代价是访问 volatile 字段时会对性能产生更大的影响。