我对Java中的某些行为感到非常困惑,我想知道是否有人可以提供解释。我正在尝试将boolean
值设置为true
以停止线程,但分配失败。请考虑以下示例:
public class Temp {
public class Unstoppable implements Runnable {
public boolean stop=false;
private int ctr=0;
@Override
public void run() {
while(!stop) {
stop |= doSomething();
}
}
public boolean doSomething() {
System.out.println("Still running "+ctr++);
// some other logic here could decide that it's time to stop
// especially if Unstoppable would be an abstract class and doSomething() an abstract function
return false;
}
public void stop() {
stop=true;
}
}
public void start() {
// start thread with Unstoppable
Unstoppable st = new Unstoppable();
new Thread(st).start();
// wait for a while
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// try to stop the thread
st.stop(); // assignment fails, variable 'stop' is still false after this call so Unstoppable never stops
}
public static void main(String[] args) {
Temp t = new Temp();
t.start();
}
}
尝试在true
函数中分配值stop()
只是失败并且线程一直在运行。我发现将代码更改为以下内容可以解决问题:
@Override
public void run() {
while(!stop) {
// without stop |= the thread DOES stop
doSomething();
}
}
但我不明白为什么。
更奇怪的是,下面的代码更改也解决了这个问题:
@Override
public void run() {
while(!stop) {
stop |= doSomething();
// printing here does also result in the thread stopping!
System.out.println("Still running "+ctr++);
}
}
public boolean doSomething() {
// some other logic here could decide that it's time to stop
// especially if Unstoppable would be an abstract class and doSomething() an abstract function
return false;
}
虽然我可以解决问题,但我想了解这里发生了什么。谢谢!
修改 再澄清一下,我将代码更改为以下内容:
public class Temp {
public class Unstoppable implements Runnable {
private volatile boolean stop=false;
@Override
public void run() {
while(!stop) {
System.out.println("A) stop="+stop);
stop |= doSomething();
System.out.println("C) stop="+stop);
}
}
public boolean doSomething() {
while(!stop) {
}
System.out.println("B) stop="+stop);
// some other logic here could decide that it's time to stop
// especially if Unstoppable would be an abstract class and doSomething() an abstract function
return false;
}
public void setStop(boolean stop) {
System.out.println("D) stop="+stop);
this.stop=stop;
System.out.println("E) stop="+stop);
}
}
public void start() {
// start thread with Unstoppable
Unstoppable st = new Unstoppable();
Thread t = new Thread(st);
t.start();
// wait for a while
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// try to stop the thread
st.setStop(true); // assignment fails, variable 'stop' is still false after this call so Unstoppable never stops
}
public static void main(String[] args) {
Temp t = new Temp();
t.start();
}
}
这导致控制台上的以下语句:
A) stop=false
D) stop=true
E) stop=true
B) stop=true
C) stop=false
A) stop=false
困惑在于声明C)stop = false。在B)它是真的,然后该函数结果为假,我希望true |= false
导致true
......
然而,正如苗条所示,在调用doSomething()之前,已经用Java评估了| =的左侧。将代码更改为:
@Override
public void run() {
while(!stop) {
boolean stopNow = doSomething();
stop |= stopNow;
}
}
导致线程停止。
答案 0 :(得分:3)
stop |= foo()
...是:
的缩写boolean x = foo();
boolean y = stop || x;
stop = y;
现在考虑两个线程:
Thread A | Thread B
1 boolean x = foo(); |
2 boolean y = stop || x; |
3 | stop = true;
4 stop = y |
5 if(stop) { ... }
如果y
是false
,那么当事情按此顺序发生时,线程B对stop
(3)的赋值将被替换为线程A的赋值(4),在测试之前(5)。
即使stop
不稳定,也会发生这种竞争条件,即使您忽略了线程之间变量可见性的“怪异”。
关键是stop |= foo()
不是原子,因此在执行过程中会发生一些事情,这会搞砸明显的逻辑。这就是为什么我们有像AtomicBoolean
这样的类,它们提供了可以用于此目的的有保证的原子操作。
AtomicBoolean stop = new AtomicBoolean();
...
while(! stop.get()) {
...
stop.compareAndSet(false, foo());
}
或者,您可以将|=
置于synchronized
方法中,然后将唯一的方式分配给stop
:
private synchronized stopIf(boolean doStop) {
this.stop |= doStop;
}