我编写了简单的多线程应用程序,只是为了解决并发问题,但我遇到了一个布尔变量的问题,它控制了线程中的循环。其中一个函数应该停止线程,如果队列中没有留下任何元素,我想这是我的问题,因为如果我在大括号之间添加一些东西:
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);
}
}
}
}
答案 0 :(得分:5)
这是一个竞争条件问题。可能在主线程中的turnOff输入之后执行run方法(另一个线程),因此标志isRunning再次设置为true并且循环永远不会结束。
这可以解释为什么使用简单的System.out.println(“ASD”)变得更好:isRunning = false被延迟。
答案 1 :(得分:3)
您的代码中存在很多问题。
turnOff
和wait
queue
和turnOff
run
的非同步访问权限
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();
}
}
答案 2 :(得分:1)
主要问题是添加/删除元素和检查队列是否为空之间的竞争条件。换句话说:
在add
块中包含remove
和synchronized
调用可以保证所有这些方法的调用都会按顺序执行。但是,在queue
块之外还有一个synchronized
变量的访问权限 - 它是queue.isEmpty()
。这意味着某个线程有可能获得此调用的结果,并且当它在if
块内执行操作时,其他线程可能会添加或删除元素。
此代码还有一些并发性问题,如果您希望对它们进行讨论,请告诉我们(它们有点偏离主题)。
答案 3 :(得分:0)
正如Germann Arlington指出的那样,queue.isEmpty()的值似乎被缓存在主线程中。尝试同步它:
while (true) {
synchronized(queue) {
if(queue.isEmpty())
break;
}
}
或者只是让队列变得不稳定:
private volatile static List<String> queue = new LinkedList<String>();
答案 4 :(得分:0)
这将解决您的问题。
在turnOff()方法的while循环中也使用volatile变量isRunning。
public void turnOff() {
while (isRunning && !queue.isEmpty()) {
}
System.out.println("end");
isRunning = false;
}