为什么volatile不能正常工作

今天我使用TimerTask创建了一个超时作业,但是遇到了一个新问题,我有一个static volatile boolean变量flag 。 我的理解是,一旦该变量的值发生变化,它就会被所有正在运行的thread通知。 但是当我运行这个程序时,我的输出低于输出,这是不可接受的。

O / P:

 -------------- -------------- DD BB Exiting process.. CC 

我的期望是我的最后一次打印应该是退出流程..为什么这种奇怪的行为?

我的代码是:

 public class TimeOutSort { static volatile boolean flag = false; public static void main(String[] args) { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { flag = true; System.out.println("Exiting process.."); // System.exit(0); } }, 10 * 200); new Thread(new Runnable() { @Override public void run() { while (!flag) System.out.println("BB"); } }).start(); new Thread(new Runnable() { @Override public void run() { while (!flag) System.out.println("CC"); } }).start(); new Thread(new Runnable() { @Override public void run() { while (!flag) System.out.println("DD"); } }).start(); } } 

编辑:我怎样才能实现这一目标?

volatile几乎意味着每次线程访问一个变量时,它必须确保使用每个线程可见的版本(即没有每个线程的缓存)。

flag设置为true后,这不会强制CC打印线程实际上立即运行。 完全可能(特别是在单核机器上)一个线程设置标志在CC打印线程甚至有机会运行之前打印消息。

另外:请注意,打印到System.out涉及获取锁( println()调用内部的某个位置),这可以修改测试代码的multithreading行为。

线程可以按任何顺序执行代码

 thread BB: while (!flag) // as flag is false thread Main: flag = true; thread Main: System.out.println("Exiting process.."); thread BB: System.out.println("BB"); 

我的期望是我的最后一次印刷应该是退出流程..

线程设计为同时并独立运行。 如果这始终是最后一个语句,那将是令人惊讶的,因为您在设置标志时无法确定每个线程的位置。

打印“CC”的线程在打印出“退出进程…”的线程打印之后才发生接收任何CPU时间。 这是预期的行为。

它不易挥发不工作(如果不是,你的一些线程不会停止)。 它是关于不同线程中指令的执行顺序,这是随机的(取决于OS调度),除非您在中间步骤显式同步循环。

要为你得到的解释添加一个替代措辞:在你的示例输出中,打印"CC"的线程while (!flag)System.out.println() 之间的行之间 “暂停”(某处System.out.println() 。 这意味着在唤醒之后, println()在下一次检查标志之前执行。 (它也不会因为你改变标志值而被唤醒,但是因为其他一些线程会阻塞或耗尽它的时间片。)

我没有测试它,但你可能会这样做

 public class TimeOutSort { static volatile boolean flag = false; public static void main(String[] args) { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { synchronized(flag){ flag = true; notifyAll(); } } }, 10 * 200); new Thread(new Runnable() { @Override public void run() { synchronized(flag){ if(!flag) { wait(); } System.out.println("BB"); } } }).start(); new Thread(new Runnable() { @Override public void run() { synchronized(flag){ if(!flag) { wait(); } System.out.println("CC"); } } }).start(); new Thread(new Runnable() { @Override public void run() { synchronized(flag){ if(!flag) { wait(); } System.out.println("DD"); } } }).start(); } 

}