簡體   English   中英

static volatile boolean - 線程沒有被終止

[英]static volatile boolean - thread not getting terminated

我編寫了簡單的多線程應用程序,只是為了解決並發問題,但我遇到了一個boolean變量的問題,它控制了線程中的循環。 其中一個函數應該停止線程,如果隊列中沒有留下任何元素,我想這是我的問題,因為如果我在大括號之間添加一些內容:

while (!queue.isEmpty()) {
}
isRunning = false;

所以它變成:

while (!queue.isEmpty()) {
    System.out.println("ASD");
}
isRunning = false;

它工作得更好 - 程序在執行turnOff方法后終止

有任何想法嗎?

這是我的應用程序的完整代碼:

package test;

public class xxx {
    public static void main(String[] args) {
        Foo instance = Foo.getInstance();
        Thread x = new Thread(instance);
        x.start();

        for (int count = 1; count < 100000; count++)
            instance.addToQueue(count + "");
        instance.turnOff();
    }
}

和:

package test;

import java.util.LinkedList;
import java.util.List;

public class Foo implements Runnable {
    private static Foo inner = null;
    private static List<String> queue = new LinkedList<String>();
    private volatile static boolean isRunning = false;

    private Foo() { }

    public static Foo getInstance() {
        if (inner == null) {
            inner = new Foo();
        }
        return inner;
    }

    public void addToQueue(String toPrint) {
        synchronized (queue) {
            queue.add(toPrint);
        }

    }

    public void removeFromQueue(String toRemove) {
        synchronized (queue) {
            queue.remove(toRemove);
        }
    }

    public void turnOff() {
        while (!queue.isEmpty()) {
        }
        System.out.println("end");
        isRunning = false;
    }

    @Override
    public void run() {
        isRunning = true;
        while (isRunning) {
            if (!queue.isEmpty()) {
                String string = queue.get(0);
                System.out.println(string);
                removeFromQueue(string);
            }

        }
    }
}

這是一個競爭條件問題。 可能在主線程中的turnOff輸入之后執行run方法(另一個線程),因此標志isRunning再次設置為true並且循環永遠不會結束。

這可以解釋為什么使用簡單的System.out.println(“ASD”)變得更好:isRunning = false被延遲。

您的代碼中存在很多問題。

  1. 在繁忙的環路turnOffwait
  2. turnOffrunqueueturnOff同步訪問
  3. 非易失性,非最終訪問inner
  4. 毋靜態isRunningqueue變量
  5. turnOffstart調用之間的競爭條件

其中一些在這個特定的實例中是無害的(例如, instance總是從主線程訪問),但是根據您的硬件配置,您將被其余部分的某些組合所咬。 添加System.out “修復”問題的原因是它使其中一個繁忙的循環不那么忙(修復1)並且具有內部同步機制(修復2),但其他循環仍在那里。

我建議刪除isRunning變量和queue.isEmpty()測試並替換為CountDownLatch

package test;

import java.util.LinkedList;
import java.util.List; 
import java.util.concurrent.CountDownLatch;

public class Foo implements Runnable {
    private static final Foo inner = new Foo();
    private final List<String> queue = new LinkedList<String>();
    private final CountDownLatch latch = new CountDownLatch(1);

    private Foo() { }

    public static Foo getInstance() {
        return inner;
    }

    public void addToQueue(String toPrint) {
        synchronized (queue) {
            queue.add(toPrint);
        }
    }

    public void removeFromQueue(String toRemove) {
        synchronized (queue) {
            queue.remove(toRemove);
        }
    }

    public boolean isEmpty() {
        synchronized (queue) {
            return queue.isEmpty();
        }
    }

    public String getHead() {
        synchronized (queue) {
            return queue.get(0);
        }
    }

    public void turnOff() throws InterruptedException {
        latch.await();
        System.out.println("end");
    }

    @Override
    public void run() {
        while (!isEmpty()) {
            String string = getHead();
            System.out.println(string);
            removeFromQueue(string);
        }

        latch.countDown();
    }
}

和跑步者

package test;

public class XXX {
    public static void main(String[] args) throws InterruptedException {
        Foo instance = Foo.getInstance();
        Thread x = new Thread(instance);

        for (int count = 1; count < 100000; count++)
            instance.addToQueue(count + "");

        x.start();
        instance.turnOff();
    }
}   

主要問題是添加/刪除元素和檢查隊列是否為空之間的競爭條件。 換句話說:

synchronized塊中包裝addremove調用可以保證將按順序執行這些方法的所有調用。 但是,還有一個對synchronized塊之外的queue變量的訪問 - 它是queue.isEmpty() 這意味着某個線程有可能獲得此調用的結果,並且當它在if塊內執行操作if ,其他線程可能會添加或刪除元素。

此代碼還有一些更多的並發問題,如果您想要討論它們,請告訴我們(它們有點偏離主題)。

正如Germann Arlington指出的那樣,queue.isEmpty()的值似乎被緩存在主線程中。 嘗試同步它:

while (true) {
    synchronized(queue) {
        if(queue.isEmpty())
            break;
    }
} 

或者只是讓隊列變得不穩定:

private volatile static List<String> queue = new LinkedList<String>();

這將解決您的問題。

在turnOff()方法的while循環中也使用volatile變量isRunning。

public void turnOff() {
    while (isRunning && !queue.isEmpty()) {
    }
    System.out.println("end");
    isRunning = false;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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