Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么volatile不能正常工作_Java_Multithreading_Static_Volatile - Fatal编程技术网

Java 为什么volatile不能正常工作

Java 为什么volatile不能正常工作,java,multithreading,static,volatile,Java,Multithreading,Static,Volatile,今天,我使用TimerTask创建了一个超时作业,但遇到了一个新问题,我遇到了一个static volatile booleanvariableflag。我的理解是,一旦该变量的值发生更改,所有运行的线程都会通知它。但当我运行这个程序时,我得到了低于输出值的结果,这是不可接受的 O/p: -------------- -------------- DD BB Exiting process.. CC 我的期望是我的最后一张打印应该是退出流程。为什么会有这种奇怪的行为 我的代码是: public

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

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

}

编辑:我该如何实现这一点?

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

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

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


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

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

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”
的线程被挂起(在某个地方)在(!flag)
System.out.println()
之间的行
while(!flag)
。这意味着在它被唤醒之后,
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();
}

}

因为线程执行顺序没有得到保证!!你是说
volatile
不起作用还是你没有正确使用它?;)我不是在谈论顺序,我只是好奇我的标志一变为真,为什么while循环中的语句会被执行?@PeterLawrey,可能是,我能够找出问题所在在编辑中回答问题后,你不应该真正修改问题的实质内容。你要求解释你观察到的行为,你得到了非常好的答案;移动门柱是不公平的。无论同步如何,随机性仍然存在。同步保证一次只执行一个线程,但并不保证线程A先执行线程B,等等,@thinkstip我指的是一般意义上的“同步”——使用锁存、屏障或任何其他机制,允许一个线程控制另一个线程的执行,而不是简单的互斥(在循环中使用“同步”块),正如您正确指出的那样,这并不能解决这里的排序问题。@amingh正确的同步。在这种情况下,让
TimerTask
等待其他任务完成使用。我建议您也点击一下,这就是主题的内容。但是它们会按顺序执行。@amingh-Huh?不,我是说同步“使用线程时知道自己在做什么”,而不是Java为此选择的关键字所暗示的“互斥”的意义?