volatile
我们是否应该像在多个线程中使用实例一样声明私有字段?
在Effective Java中,有一个示例,如果没有 volatile,代码将无法工作:
import java.util.concurrent.TimeUnit;
// Broken! - How long would you expect this program to run?
public class StopThread {
private static boolean stopRequested; // works, if volatile is here
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
public void run() {
int i = 0;
while (!stopRequested)
i++;
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
解释说
while(!stopRequested)
i++;
被优化为这样的东西:
if(!stopRequested)
while(true)
i++;
所以后台线程看不到进一步的修改stopRequested
,所以它永远循环。(顺便说一句,该代码在没有volatile
JRE7 的情况下终止。)
现在考虑这个类:
public class Bean {
private boolean field = true;
public boolean getField() {
return field;
}
public void setField(boolean value) {
field = value;
}
}
和一个线程如下:
public class Worker implements Runnable {
private Bean b;
public Worker(Bean b) {
this.b = b;
}
@Override
public void run() {
while(b.getField()) {
System.err.println("Waiting...");
try { Thread.sleep(1000); }
catch(InterruptedException ie) { return; }
}
}
}
上面的代码在不使用 volatile 的情况下按预期工作:
public class VolatileTest {
public static void main(String [] args) throws Exception {
Bean b = new Bean();
Thread t = new Thread(new Worker(b));
t.start();
Thread.sleep(3000);
b.setField(false); // stops the child thread
System.err.println("Waiting the child thread to quit");
t.join();
// if the code gets, here the child thread is stopped
// and it really gets, with JRE7, 6 with -server, -client
}
}
我认为由于 public setter,编译器/JVM 永远不应该优化调用的代码getField()
,但是这篇文章说有一些“Volatile Bean”模式(模式#4),应该应用于创建可变线程安全类. 更新:也许那篇文章仅适用于 IBM JVM?
问题是:JLS 的哪一部分明确或隐含地说必须将具有公共 getter/setter 的私有原始字段声明为volatile
(或者它们不必声明)?
抱歉问了一个很长的问题,我试图详细解释这个问题。如果有不清楚的地方,请告诉我。谢谢。