在“Effective Java”一书中: // Broken! - How long would you expect this program to run?
public class StopThread {
private static boolean stopRequested;
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;
}
}
backgroundThread在一秒钟后不会停止.因为吊装,在JVM,HotSpot服务器VM中进行优化. 您可以在以下主题中查看此内容: Why HotSpot will optimize the following using hoisting?.
优化如下: if (!done)
while (true)
i ;
有两种方法可以解决问题. 1.使用挥发性 private static volatile boolean stopRequested;
volatile的功能是 – 禁止吊装 – 它保证读取该字段的任何线程都将看到最近写入的值 2.使用同步 public class StopThread {
private static boolean stopRequested;
private static synchronized void requestStop() {
stopRequested = true;
}
private static synchronized boolean stopRequested() {
return stopRequested;
}
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);
requestStop();
}
}
上面的代码正好在Effective Java书中,它相当于使用volatile来装饰stopRequested. private static boolean stopRequested() {
return stopRequested;
}
如果此方法省略synchronized关键字,则此程序运行不正常. 我认为当方法省略synchronized关键字时,此更改会导致提升. 是对的吗? 解决方法: 要清楚地理解为什么会发生这种情况,您需要了解更深层次发生的事情. (这基本上是对所谓之前发生的关系的解释,我希望这种语言对于读者更为明白). 通常,变量存在于RAM存储器中.当一个线程需要使用它们时,它会从RAM中取出它们并将它们放入缓存中,以便它可以尽快访问它们直到需要它们为止. 使用volatile强制一个线程直接从RAM内存中读取和写入变量.因此,当许多线程使用相同的volatile变量时,它们都会看到RAM内存中存在的最后一个版本,而不是缓存中可能的旧副本. 当线程进入同步块时,它需要控制监视器变量.所有其他线程一直等到第一个线程从synchronized块退出.为确保所有线程都能看到相同的修改,同步块中使用的所有变量都直接从RAM内存中读取和写入,而不是从高速缓存副本中读取. 因此,如果您尝试在没有synchronized方法或没有volatile关键字的情况下读取变量stopRequested,则可以读取缓存中存在的可能的旧副本. 要解决这个问题,您需要确保: >所有线程都使用volatile变量 >或访问这些变量的所有线程都使用同步块. 使用方法 private static boolean stopRequested() {
return stopRequested;
}
如果没有synchronized关键字,并且stopRequested不是volatile,则意味着您可以从无效的缓存副本中读取stopRequested的值. 来源:https://www./content-3-274801.html
|