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
variableflag
。我的理解是,一旦该变量的值发生更改,所有运行的线程都会通知它。但当我运行这个程序时,我得到了低于输出值的结果,这是不可接受的
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为此选择的关键字所暗示的“互斥”的意义?