[英]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 |= false
會導致true
...
但是,如苗條所示,Java在調用doSomething()之前已經對| =的左側進行了評估。 將代碼更改為:
@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
,那么,當事情按此順序發生時,在測試(5)之前,線程B對stop
(3)的分配被線程A的賦值(4)代替。
即使stop
是volatile,並且即使您忽略線程之間變量可見性的“怪異”,也會發生這種競爭情況。
關鍵是stop |= foo()
不是原子的 ,因此在其執行期間可能會發生事情,從而破壞了明顯的邏輯。 這就是為什么我們有諸如AtomicBoolean
類的類,這些類提供了可以用於此目的的有保證的原子操作。
AtomicBoolean stop = new AtomicBoolean();
...
while(! stop.get()) {
...
stop.compareAndSet(false, foo());
}
或者,您可以將|=
放入synchronized
方法中,並使其成為分配stop
的唯一方法 :
private synchronized stopIf(boolean doStop) {
this.stop |= doStop;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.