被Java迷惑:Thread中的boolean赋值失败
Puzzled by Java: boolean assignment fails in Thread
我对 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();
}
}
尝试在 stop()
函数中分配值 true
完全失败,线程保持 运行。我发现将代码更改为以下可以解决问题:
@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
...
然而,正如 slim 所示,在调用 doSomething() 之前,Java 已经计算了 |= 的左侧。将代码更改为:
@Override
public void run() {
while(!stop) {
boolean stopNow = doSomething();
stop |= stopNow;
}
}
是否导致线程停止。
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
是易变的,即使您忽略线程之间变量可见性的 "weirdness",也会发生这种竞争情况。
关键是 stop |= foo()
不是 原子的 ,因此在执行期间可能会发生一些事情,从而搞砸了明显的逻辑。这就是为什么我们有 类 之类的 AtomicBoolean
,它们提供有保证的原子操作,您可以将其用于此目的。
AtomicBoolean stop = new AtomicBoolean();
...
while(! stop.get()) {
...
stop.compareAndSet(false, foo());
}
或者,您可以将 |=
放入 synchronized
方法中,并使其成为您分配 stop
的 唯一方法 :
private synchronized stopIf(boolean doStop) {
this.stop |= doStop;
}
我对 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();
}
}
尝试在 stop()
函数中分配值 true
完全失败,线程保持 运行。我发现将代码更改为以下可以解决问题:
@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
...
然而,正如 slim 所示,在调用 doSomething() 之前,Java 已经计算了 |= 的左侧。将代码更改为:
@Override
public void run() {
while(!stop) {
boolean stopNow = doSomething();
stop |= stopNow;
}
}
是否导致线程停止。
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
是易变的,即使您忽略线程之间变量可见性的 "weirdness",也会发生这种竞争情况。
关键是 stop |= foo()
不是 原子的 ,因此在执行期间可能会发生一些事情,从而搞砸了明显的逻辑。这就是为什么我们有 类 之类的 AtomicBoolean
,它们提供有保证的原子操作,您可以将其用于此目的。
AtomicBoolean stop = new AtomicBoolean();
...
while(! stop.get()) {
...
stop.compareAndSet(false, foo());
}
或者,您可以将 |=
放入 synchronized
方法中,并使其成为您分配 stop
的 唯一方法 :
private synchronized stopIf(boolean doStop) {
this.stop |= doStop;
}