public class Stuff {
private final Timer timer = new Timer(true);
public static final int DEFAULT_TIMEOUT = 1500;
private volatile int timeout = DEFAULT_TIMEOUT;
public void doStuff(OtherStuff) {
...
timer.schedule(timeout, ...);
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public int getTimeout() {
return timeout;
}
}
这个类的实例只能从一个线程访问,除了可以从另一个类更改的超时变量。在我的例子中是一个 JMX bean,这意味着可以在运行时从管理界面更改超时。
doStuff() 可以每秒运行 100 次,而 setTimeout() 可以每周运行一次 - 因此执行 setTimeout() 的人和执行 doWork() 的人之间的顺序并不重要。
timeout
volatile 是否足以应对这种情况?内存模型是否保证从一个线程将其设置为对 doStuff()
方法可见?
另一种看似安全的替代方法就是:
public class Stuff {
private final Timer timer = new Timer(true);
public static final int DEFAULT_TIMEOUT = 1500;
private int timeout = DEFAULT_TIMEOUT;
public void doStuff(OtherStuff) {
...
timer.schedule(getTimeout(), ...);
}
public void synchronized setTimeout(int timeout) {
this.timeout = timeout;
}
public int synchronized getTimeout() {
return timeout;
}
}
这两种方法中哪一种是首选?
最佳答案
从可见性的角度来看,这两种方法是等效的。对同一个 volatile 变量的写入之后发生的对 volatile 的任何读取都保证可以看到写入。
因此,如果一个线程写入 timeout = newValue;
,任何其他随后调用 timer.schedule(timeout)
的线程都保证看到 newValue
.
此保证在 JLS 17.4.5 中指定:
A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.
我会简单地使用 volatile,因为它提供的保证已经足够并且清楚地表明了您的意图。
关于Java,volatile也会保证可见性吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13592253/