2

今天我正在使用创建一个超时作业,TimerTask但遇到了一个新问题,我有一个static volatile boolean变量 flag。我的理解是,一旦此变量的值发生更改,所有正在运行的thread. 但是当我运行这个程序时,我得到了低于输出的结果,这是不可接受的。

输出/输出:

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

我的期望是我的最后一次打印应该是Exiting process..为什么会出现这种奇怪的行为?

我的代码是:

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

}

编辑:我怎样才能做到这一点?

4

6 回答 6

3

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

不会强制 CC 打印线程设置flagtrue. 完全有可能(尤其是在单核机器上)一个线程在 CC 打印线程甚至有机会运行之前设置标志打印消息。

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

于 2012-07-26T13:41:14.203 回答
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");

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

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

于 2012-07-26T13:42:12.463 回答
1

打印“CC”的线程碰巧没有收到任何 CPU 时间,直到打印“退出进程...”的线程打印出来。这是预期的行为。

于 2012-07-26T13:40:32.447 回答
1

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

于 2012-07-26T13:42:12.880 回答
1

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

于 2012-07-26T13:46:56.433 回答
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();
}

}

于 2015-02-01T16:55:34.563 回答