我正在使用while循环来知道何时停止应用程序并且我正在为线程使用执行程序服务:
ExecutorService executorService = Executors.newFixedThreadPool(8);
String[] error = {""};
while(!(error[0].equals("Nothing found"))) {
executorService.execute(new Runnable() {
@Override
public void run() {
addProductsToSet();
//rest of the code
synchronized (error[0]) {
error[0] = // code that might return "Nothing found" to stop the while loop
}
//rest of the code
} //end of run method
});
}
executorService.shutdown();
while(!executorService.isTerminated()) {
}
我遇到的问题是error[0]
的值为“Nothing found”,但代码并没有停止。它会一直持续下去。如果我没有使用线程,则while循环停止。我也试过executorService.shutdownNow()
但是没有用。
有什么建议吗?
答案 0 :(得分:1)
您的代码存在的一个基本问题是,您正在尝试同步“任何对象是数组的第0个元素”,但该元素本身可能会发生变化。另一个问题是,当您尝试读取错误状态时,您根本没有同步任何内容。
如果你真的必须使用String作为是否以这种方式继续的标记,那么解决方案是使用一个简单的String变量,但将其声明为volatile,或者查看AtomicReference类。
也就是说,我强烈建议尝试在此处更改程序流程以使用Callable接口而不是Runnable。您将Callable传递给Executor.submit()方法,并且有效地允许您执行的操作是让线程中的任务“正确”将错误(作为例外)传递回发布线程(运行您的循环的线程)在这种情况下)。这样,你会避免潜在的同步错误,我认为你的程序流程会更加清晰。
答案 1 :(得分:1)
您的代码中存在两个问题。首先,您依赖于从另一个线程可以看到对error[0]
的非易失性变量写入。你不能依赖它。 JVM可以优化您的代码以仅读取error[0]
一次,因为它不保证另一个线程中的写入将是可见的。请注意,数组元素始终是非易失性的,因此您无法使用数组正确执行此操作。正确的方法是使用AtomicReference
:
AtomicReference<String> error = new AtomicReference<>("");
while (!(error.get().equals("Nothing found"))) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(i.incrementAndGet());
addProductsToSet();
// rest of the code
error.set("Nothing found");
// rest of the code
} // end of run method
});
}
虽然可能只有两个状态(空字符串和"Nothing found"
),但AtomicBoolean
可能是最佳解决方案。当然,您不应该在AtomicReference
上进行同步。
第二个问题是你甚至在开始执行之前创建了大量的任务。你的while
循环只是在没有任何等待的情况下将任务提交到池中,所以当至少有一个任务真正启动并且能够说"Nothing found"
时,你将在池中拥有数千个任务,你将拥有等到所有人都在shutdown()
结束。您可以通过将shutdown()
更改为shutdownNow()
来快速解决此问题,但我想这只会隐藏原始问题。你要做的实际事情就是重新考虑你是否需要这么多的任务。
答案 2 :(得分:0)
整个模式看起来有点瑕疵。我会建议像这样的模式:
ExecutorService executorService = Executors.newFixedThreadPool(4);
executorService.execute(new Runnable() {
@Override
public void run() {
while(true) {
addProductsToSet();
// rest of the code
String error = code that might return "Nothing found" to stop the while loop
if(error.equals("Nothing found")) {
break;
}
}
// rest of the code
} // end of run method
});
executorService.shutdown();
while (!executorService.isTerminated()) {
}
这样,如果方法返回“Nothing found”并且在每次while循环迭代中没有启动新的Threadpool,则每个Thread都会停止。