为什么volatile不能正常工作

时间:2012-07-26 13:37:19

标签: java multithreading static 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();
    }

}

编辑:我如何实现这一目标?

6 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:3)

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

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");
  

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

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

答案 2 :(得分:1)

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

答案 3 :(得分:1)

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

答案 4 :(得分:1)

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

答案 5 :(得分:0)

我没有测试它,但你可以像这样实现它

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();
}

}