Java 而线程循环不会停止

Java 而线程循环不会停止,java,multithreading,Java,Multithreading,我正在使用while循环来知道何时停止应用程序,并且正在为线程使用executor服务: ExecutorService executorService = Executors.newFixedThreadPool(8); String[] error = {""}; while(!(error[0].equals("Nothing found"))) { executorService.execute(new Runnable() { @Override

我正在使用while循环来知道何时停止应用程序,并且正在为线程使用executor服务:

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个元素是什么对象”上进行同步,但该元素本身可能会发生变化。另一个问题是,当您尝试读取错误状态时,您根本没有同步任何内容

如果您确实必须使用字符串作为是否以这种方式继续的标记,那么解决方案是使用简单的字符串变量,但将其声明为volatile,或者查看AtomicReference类


也就是说,我强烈建议您在这里尝试更改程序流,以使用可调用接口,而不是可运行接口。将一个可调用的函数传递到Executor.submit()方法中,这样可以让线程中的任务“正确”地将错误(作为异常)传递回发布线程(本例中运行循环的线程)。这样,您将避免潜在的同步错误,并且您的程序流程将更加清晰。

您的代码中有两个问题。首先,您依赖于非易失性变量write to
error[0]
将从另一个线程可见。你不能依赖它。JVM可能会优化您的代码,以便只读取一次
错误[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
    });
}
AtomicReference错误=新的AtomicReference(“”);
而(!(error.get().equals(“找不到”)){
executorService.execute(新的Runnable(){
@凌驾
公开募捐{
System.out.println(i.incrementAndGet());
addProductsToSet();
//代码的其余部分
设置错误(“未找到任何内容”);
//代码的其余部分
}//运行结束方法
});
}
虽然可能只有两种状态(空字符串和
未找到任何内容”
),但
原子布尔
将是最好的解决方案。当然,您不应该在
AtomicReference
上进行同步


第二个问题是,在任务开始执行之前,您创建了大量任务。您的
while
循环只是将任务提交到池中而没有任何等待,因此当至少有一个任务实际启动并且能够说
“找不到任何任务”
时,池中将有数千个任务,您必须等到所有任务在
shutdown()
中完成。您可以通过将
shutdown()
更改为
shutdownNow()
来快速修复此问题,但我想这只会隐藏原始问题。实际上你需要做的是重新考虑你是否需要这么多的任务。

整个模式看起来有点缺陷。我建议这样一种模式:

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循环迭代中都没有启动新的线程池,则每个线程都会停止。

是否尝试使
错误
变量不稳定?永远不要编写自旋循环。我强烈建议您将
while(!executorService.isTerminated())
替换为
executorService.waittermination(Long.MAX\u值,TimeUnit.millises)
,就像建议的那样。