虽然带螺纹的循环不会停止

时间:2015-07-06 12:16:32

标签: java multithreading

我正在使用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()但是没有用。

有什么建议吗?

3 个答案:

答案 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都会停止。