【发布时间】:2012-06-12 12:28:34
【问题描述】:
如果在多个线程中使用实例,我们是否应该将私有字段声明为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 的进一步修改,因此它永远循环。 (顺便说一句,该代码在 JRE7 上没有 volatile 的情况下终止。)
现在考虑这个类:
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
}
}
我认为由于公共设置器,编译器/JVM 永远不应该优化调用 getField() 的代码,但 this article 说有一些“Volatile Bean”模式(模式 #4),应该应用创建可变线程安全类。 更新:也许那篇文章仅适用于 IBM JVM?
问题是:JLS 的哪一部分明确或隐含地说,具有公共 getter/setter 的私有原始字段必须声明为 volatile(或者它们不必)?
对不起,问题很长,我试图详细解释这个问题。如果有不清楚的地方,请告诉我。谢谢。
【问题讨论】:
-
不需要那个字段来取消线程,你可以在线程上使用中断标志。
-
@NathanHughes,这些类只是最小的例子,实际代码不同,这里不需要线程中断。
标签: java volatile memory-model java-memory-model