Java:与CountDownLatch的ExecutorService线程同步导致死锁?

时间:2012-04-23 15:11:45

标签: java deadlock executorservice countdownlatch

我为编程实践编写了一个生命游戏。生成器有3种不同的实现方式。第一:一个主线程+ N个子线程,第二个:SwingWorker + N个子线程,第三个:SwingWorker + ExecutorService。 N是availableProcessors或用户定义的数量。 前两个实现运行正常,有一个或多个线程。 ExecutorServise的实现在一个线程上运行正常,但是锁定多个线程。我尝试了一切,但我无法得到解决方案。

这里是精细工作实现的代码(第二个):

    package example.generator;

    import javax.swing.SwingWorker;

    /**
     * AbstractGenerator implementation 2: SwingWorker + sub threads.
     * 
     * @author Dima
     */
    public final class WorldGenerator2 extends AbstractGenerator {


        /**
         * Constructor.
         * @param gamePanel The game panel
         */
        public WorldGenerator2() {
            super();
        }




        /* (non-Javadoc)
         * @see main.generator.AbstractGenerator#startGenerationProcess()
         */
        @Override
        protected void startGenerationProcess() {
            final SwingWorker<Void, Void> worker = this.createWorker();
            worker.execute();
        }




        /**
         * Creates a swing worker for the generation process.
         * @return The swing worker
         */
        private SwingWorker<Void, Void> createWorker() {
            return new SwingWorker<Void, Void>() {

                @Override
                protected Void doInBackground() throws InterruptedException {
                    WorldGenerator2.this.generationProcessing();
                    return null;
                }
            };
        }





        /* (non-Javadoc)
         * @see main.generator.AbstractGenerator#startFirstStep()
         */
        @Override
        public void startFirstStep() throws InterruptedException {
            this.getQueue().addAll(this.getLivingCells());
            for (int i = 0; i < this.getCoresToUse(); i++) {
                final Thread thread = new Thread() {
                    @Override
                    public void run() {
                        WorldGenerator2.this.fistStepProcessing();
                    }
                };
                thread.start();
                thread.join();
            }
        }




        /* (non-Javadoc)
         * @see main.generator.AbstractGenerator#startSecondStep()
         */
        @Override
        protected void startSecondStep() throws InterruptedException {
            this.getQueue().addAll(this.getCellsToCheck());
            for (int i = 0; i < this.getCoresToUse(); i++) {
                final Thread thread = new Thread() {

                    @Override
                    public void run() {
                        WorldGenerator2.this.secondStepProcessing();
                    }
                };
                thread.start();
                thread.join();
            }
        }

    }

以下是使用执行程序服务的不工作实现的代码:

    package example.generator;

    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;

    import javax.swing.SwingWorker;

    /**
     * AbstractGenerator implementation 3: SwingWorker + ExecutorService.
     * 
     * @author Dima
     */
    public final class WorldGenerator3 extends AbstractGenerator {


        private CountDownLatch  countDownLatch;
        private ExecutorService executor;


        /**
         * Constructor.
         * @param gamePanel The game panel
         */
        public WorldGenerator3() {
            super();
        }




        /* (non-Javadoc)
         * @see main.generator.AbstractGenerator#startGenerationProcess()
         */
        @Override
        protected void startGenerationProcess() {
            this.executor = Executors.newFixedThreadPool(this.getCoresToUse());
            final SwingWorker<Void, Void> worker = this.createWorker();
            worker.execute();
        }




        /**
         * Creates a swing worker for the generation process.
         * @return The swing worker
         */
        private SwingWorker<Void, Void> createWorker() {
            return new SwingWorker<Void, Void>() {

                @Override
                protected Void doInBackground() throws InterruptedException {
                    WorldGenerator3.this.generationProcessing();
                    return null;
                }
            };
        }




        /* (non-Javadoc)
         * @see main.generator.AbstractGenerator#startFirstStep()
         */
        @Override
        public void startFirstStep() throws InterruptedException {
            this.getQueue().addAll(this.getLivingCells());
            this.countDownLatch = new CountDownLatch(this.getCoresToUse());
            for (int i = 0; i < this.getCoresToUse(); i++) {    
                this.executor.execute(new Runnable() {  
                    @Override
                    public void run() {
                        WorldGenerator3.this.fistStepProcessing();
                        WorldGenerator3.this.countDownLatch.countDown();
                    }
                });
            }
            this.countDownLatch.await();

        }




        /* (non-Javadoc)
         * @see main.generator.AbstractGenerator#startSecondStep()
         */
        @Override
        protected void startSecondStep() throws InterruptedException {
            this.getQueue().addAll(this.getCellsToCheck());
            this.countDownLatch = new CountDownLatch(this.getCoresToUse());
            for (int i = 0; i < this.getCoresToUse(); i++) {
                this.executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        WorldGenerator3.this.secondStepProcessing();
                        WorldGenerator3.this.countDownLatch.countDown();
                    }
                });
            }
            this.countDownLatch.await();

        }
    }

在这里,您可以使用小型启动器下载我的应用程序示例。它仅在控制台上打印迭代结果:Link


现在我的代码看起来像这样:

/* (non-Javadoc)
 * @see main.generator.AbstractGenerator#startFirstStep()
 */
@Override
public void startFirstStep() throws InterruptedException {

    this.getQueue().addAll(this.getLivingCells());      

    final ArrayList<Callable<Void>> list = new ArrayList<Callable<Void>>(this.getCoresToUse());

    for (int i = 0; i < this.getCoresToUse(); i++) {

        list.add(new Callable<Void>() {

                @Override
                public Void call() throws Exception {
                    WorldGenerator3.this.fistStepProcessing();
                    return null;
                }
            }
        );          
    }

    this.executor.invokeAll(list);
}

但这又是同样的问题。如果我用一个核心(线程)运行它没有问题。如果我将核心数设置为多个,则会锁定。在我的第一个问题中,有一个示例的链接,您可以运行(在eclipse中)。也许我忽略了之前代码中的内容。

2 个答案:

答案 0 :(得分:1)

在所有变体中,您都是以串行而非并行方式执行线程,因为for循环中有joinawait。这意味着for循环无法继续下一次迭代,直到刚刚启动的线程完成。这相当于在任何给定时间只有一个线程存活 - 主线程或在当前循环迭代中创建的一个线程。如果你想加入多个线程,你必须收集它们的引用,然后,在你启动它们的循环之外,进入另一个循环,你加入每个线程。

至于在CountDownLatch变体中使用Executors,对于线程所说的是这里的锁存器:不要使用实例var;使用收集所有锁存器的本地列表并在单独的循环中等待它们。

但是,您不应该首先使用CountDownLatch:您应该将所有并行任务放在Callable列表中并使用ExecutorService.invokeAll调用{{1}}。它将自动阻止,直到完成所有任务。

答案 1 :(得分:1)

我发现你对Executors设施的使用有点奇怪......

即。我们的想法是让Executor拥有一个线程池,其大小通常与CPU支持的内核数量有关。 然后向Executor提交任意数量的并行任务,让它决定从池中可用线程执行的时间和内容。

至于CountDownLatch ......为什么不使用ExecutorService.invokeAll?此方法将阻止直到所有提交的任务完成或达到超时。所以它会计算代表你的工作。 或者CompletionService如果你希望在任务结果可用时立即使用它就“将新异步任务的产生与已完成任务的结果的消耗分离”,即不等待所有任务首先完成。 / p>

这样的东西
    private static final int WORKER_THREAD_COUNT_DEFAULT = Runtime.getRuntime().availableProcessors() * 2;

    ExecutorService executor = Executors.newFixedThreadPool(WORKER_THREAD_COUNT);

    // your tasks may or may not return result so consuming invokeAll return value may not be necessary in your case 
    List<Future<T>> futuresResult = executor.invokeAll(tasksToRunInParallel, EXECUTE_TIMEOUT,
                TimeUnit.SECONDS);