簡體   English   中英

Java感到困惑:布爾分配在線程中失敗

[英]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) { ... }

如果yfalse ,那么,當事情按此順序發生時,在測試(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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM