[英]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被延遲。
您的代碼中存在很多問題。
turnOff
和wait
turnOff
和run
對queue
的turnOff
同步訪問 inner
isRunning
和queue
變量 turnOff
和start
調用之間的競爭條件 其中一些在這個特定的實例中是無害的(例如, 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
塊中包裝add
和remove
調用可以保證將按順序執行這些方法的所有調用。 但是,還有一個對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.