为什么没有挥发性?
Why no volatile?
我和一位同事就这段代码进行了讨论:
public final class ShutdownHookRegistration {
/**
* Global shutdown flag
*/
private static boolean isServerShutdown = false;
private ShutdownHookRegistration() {
// empty
}
/**
* Returns the current value of the global shutdown flag
*
* @return
*/
public static boolean isServerShutdown() {
return isServerShutdown;
}
/**
* Registration if shutdown hooks
*/
public static void registerShutdownHooks() {
/**
* 1. Shutdown hook to set the shutdown flag
*/
Runtime.getRuntime().addShutdownHook(setGlobalShutdownFlag());
}
/**
* Sets the global static is shutdown flag which can be checked by other processes.
*
* @return
*/
private static Thread setGlobalShutdownFlag() {
return new Thread() {
@Override
public void run() {
isServerShutdown = true;
System.out.println(Thread.currentThread().getName() + ":shutdown set");
}
};
}
public static void main(String[] args) throws InterruptedException {
System.out.println(Thread.currentThread().getName() + " Flag set:" + ShutdownHookRegistration.isServerShutdown);
Thread t1 = ShutdownHookRegistration.setGlobalShutdownFlag();
Thread t2 = new Thread() {
public void run() {
while (!ShutdownHookRegistration.isServerShutdown) {
System.out.println(Thread.currentThread().getName() + " Flag set:" + ShutdownHookRegistration.isServerShutdown);
}
}
};
t2.start();
t1.start();
}
输出:
Thread-1 Flag set:false
Thread-1 Flag set:false
Thread-1 Flag set:false
Thread-1 Flag set:false
[..]
Thread-0:shutdown set
我认为如果没有 volatile,这段代码会 运行 进入无限循环,但不知何故它总是会终止。
有人可以解释为什么这里不需要 volatile 吗?
每个线程都获取变量 isServerShutdown 的副本,JVM 可以在每个不确定的时间刷新和更新该值,许多应用程序可以工作,许多不可以,其他应用程序可以工作您在 运行 方法中添加另一个语句...
你很幸运,如果你需要应用程序正常工作,你必须使用可变变量。
标记字段volatile
...
...ensures that all threads see a consistent value for the variable (§17.4).
Not 标记它 volatile
并不一定意味着其他线程不会看到更改后的值 最终 ,只是他们可能会继续看到旧值一段时间,因为可以为线程缓存旧值(因为它不是 volatile
)。但这并不意味着它们的缓存值不会在 some 点更新。只是当另一个线程更改值时,这可能会有所删除(例如,延迟)。
如果您希望主动观察更改,请将字段设置为 volatile
,但请注意,这会影响线程 reading/writing 它(这是否是一个问题完全是另一回事)。
简而言之,有两个原因,您的循环有内存屏障,即使没有,它也 运行 不够长,无法 optimised/compiled 在需要 volatile 的情况下。
关键在这里
while (!ShutdownHookRegistration.isServerShutdown) {
System.out.println(Thread.currentThread().getName() + " Flag set:" + ShutdownHookRegistration.isServerShutdown);
}
System.out.println 是 synchronized
这意味着在每次迭代中有一个 read/write 障碍。
// from the java 6 source
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
在 x64 JVM 中,一旦锁定了一个对象,就会放置一个内存屏障来保护所有内存访问。
此外,这会使您的代码速度降低 10,000 倍或更多,因此它 运行 的时间不足以进行编译(并在需要 volatile
的问题上进行优化)它会在编译此代码之前以 10,000 次的顺序循环。
我和一位同事就这段代码进行了讨论:
public final class ShutdownHookRegistration {
/**
* Global shutdown flag
*/
private static boolean isServerShutdown = false;
private ShutdownHookRegistration() {
// empty
}
/**
* Returns the current value of the global shutdown flag
*
* @return
*/
public static boolean isServerShutdown() {
return isServerShutdown;
}
/**
* Registration if shutdown hooks
*/
public static void registerShutdownHooks() {
/**
* 1. Shutdown hook to set the shutdown flag
*/
Runtime.getRuntime().addShutdownHook(setGlobalShutdownFlag());
}
/**
* Sets the global static is shutdown flag which can be checked by other processes.
*
* @return
*/
private static Thread setGlobalShutdownFlag() {
return new Thread() {
@Override
public void run() {
isServerShutdown = true;
System.out.println(Thread.currentThread().getName() + ":shutdown set");
}
};
}
public static void main(String[] args) throws InterruptedException {
System.out.println(Thread.currentThread().getName() + " Flag set:" + ShutdownHookRegistration.isServerShutdown);
Thread t1 = ShutdownHookRegistration.setGlobalShutdownFlag();
Thread t2 = new Thread() {
public void run() {
while (!ShutdownHookRegistration.isServerShutdown) {
System.out.println(Thread.currentThread().getName() + " Flag set:" + ShutdownHookRegistration.isServerShutdown);
}
}
};
t2.start();
t1.start();
}
输出:
Thread-1 Flag set:false
Thread-1 Flag set:false
Thread-1 Flag set:false
Thread-1 Flag set:false
[..]
Thread-0:shutdown set
我认为如果没有 volatile,这段代码会 运行 进入无限循环,但不知何故它总是会终止。
有人可以解释为什么这里不需要 volatile 吗?
每个线程都获取变量 isServerShutdown 的副本,JVM 可以在每个不确定的时间刷新和更新该值,许多应用程序可以工作,许多不可以,其他应用程序可以工作您在 运行 方法中添加另一个语句...
你很幸运,如果你需要应用程序正常工作,你必须使用可变变量。
标记字段volatile
...
...ensures that all threads see a consistent value for the variable (§17.4).
Not 标记它 volatile
并不一定意味着其他线程不会看到更改后的值 最终 ,只是他们可能会继续看到旧值一段时间,因为可以为线程缓存旧值(因为它不是 volatile
)。但这并不意味着它们的缓存值不会在 some 点更新。只是当另一个线程更改值时,这可能会有所删除(例如,延迟)。
如果您希望主动观察更改,请将字段设置为 volatile
,但请注意,这会影响线程 reading/writing 它(这是否是一个问题完全是另一回事)。
简而言之,有两个原因,您的循环有内存屏障,即使没有,它也 运行 不够长,无法 optimised/compiled 在需要 volatile 的情况下。
关键在这里
while (!ShutdownHookRegistration.isServerShutdown) {
System.out.println(Thread.currentThread().getName() + " Flag set:" + ShutdownHookRegistration.isServerShutdown);
}
System.out.println 是 synchronized
这意味着在每次迭代中有一个 read/write 障碍。
// from the java 6 source
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
在 x64 JVM 中,一旦锁定了一个对象,就会放置一个内存屏障来保护所有内存访问。
此外,这会使您的代码速度降低 10,000 倍或更多,因此它 运行 的时间不足以进行编译(并在需要 volatile
的问题上进行优化)它会在编译此代码之前以 10,000 次的顺序循环。