不使用volatile关键字平台的影响是否具体?
在Ubuntu 13.04 x64上使用openJDK 1.7使用或不使用volatile关键字无效;从某种意义上说,程序在不使用volatile时按预期执行。
我想知道这个的确切原因是什么,以及为什么它不会像在Windows中使用Oracle JVM一样失败。
我知道什么是挥发性保证以及什么时候应该使用。这不是问题。
示例:
public class VolatileTest {
private static boolean test;
public static void main(String... args) {
Thread a = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
test = true;
}
});
Thread b = new Thread(new Runnable() {
@Override
public void run() {
while(!test) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(test);
}
}
});
a.start();
b.start();
}}
答案 0 :(得分:3)
Volatile在其效果方面不是特定于平台的。它符合Java Memory Model.中规定的规范。对于每个平台,JVM中的实现明显不同,关于它如何强制执行内存障碍。
您能提供示例代码吗?你的测试可能会产生误导。
答案 1 :(得分:1)
字段可以声明为volatile,在这种情况下Java内存模型可以确保 所有线程都看到变量的一致值。 - JLS 8.3.1.4
Volatile保证变量的一致视图。 volatile的本机实现禁止CPU / Core将变量保存在它的寄存器中以进行线程执行的计算,以便在其他CPU / Core上运行的所有线程可以具有该变量的一致视图。
您无法通过运行少量测试用例来确定它无法正常工作。这些问题可能会在成千上万的测试中被捕获。
答案 2 :(得分:1)
不使用volatile关键字平台的影响是否具体?
Java内存模型描述了程序在正确同步时必须的行为方式,但没有详细说明程序 的行为 从例如因果关系的要求)。所以从理论上讲,它是特定于平台的。
实际上,它是特定于平台和JVM的。通常,x86体系结构具有相当强大的内存模型,删除volatile
关键字通常不会破坏您的程序(即代码仍然表现为变量仍为volatile
)。
一些(通常较旧的)处理器甚至是顺序一致的,这意味着您的程序将表现得好像所有内容都已同步。
另一方面,在ARM等内存较弱的处理器上,更容易观察到断开的多线程程序的影响。
类似地,一些JVM在优化方面更具攻击性,并且会不同地重新排序指令,提升变量等。对于给定的JVM,您使用的参数也会产生影响。例如,在Hotspot上,如果你禁用JIT编译,你可能会“修复”一些线程安全问题。
答案 3 :(得分:0)
声明字段volatile可确保所有线程都能看到字段的当前值。不声明字段volatile不会确保其他线程不会在线程的字段上看到修改。
稍微修改一下你的例子:
public class VolatileTest {
private static boolean test = false;
public static void main(String... args) {
Thread a = new Thread(new Runnable() {
@Override
public void run() {
boolean localTest = test;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("changing local test");
test = !localTest;
}
});
Thread b = new Thread(new Runnable() {
@Override
public void run() {
boolean localTest = test;
while(!localTest) {
localTest = test;
}
}
});
a.start();
b.start();
}}
这个将多次读取字段test
,然后JVM将缓存线程test
的{{1}}值。使线程b
休眠将使b
字段无法读取太多,然后JVM将不会尝试“优化”(在线程test
上缓存test
的值) - 但如果b
被声明为volatile,则JVM无法缓存test
的值。
测试是在Ubuntu Lucid 64位上运行的,使用Oracle的Java 1.7.0。
答案 4 :(得分:0)
您提供的示例代码没有足够的循环来获得JIT优化。它将在解释器中运行,因此您的代码(很可能)不受任何会导致其失败的编译器优化技巧的影响。 CPU本身也不太可能采用足以阻止此代码的技巧,特别是考虑到这些睡眠中涉及的相对较大的时间尺度。
但是,这是特定JVM的实现细节。即使错误永远不会出现在您的特定硬件,JVM和操作系统组合上,您的程序仍然很有用。
答案 5 :(得分:0)
must
使用volatile
。 volatile提供的此行为不是特定于平台的。volatile
的情况下看到一致的最新值,但这是运气和运气。无法编程。