如何正确扩展FutureTask

时间:2013-02-27 13:04:45

标签: java executorservice swingworker futuretask

在编写计算量很大的应用程序时,我尝试使用SwingWorker类将负载分散到多个CPU核心。然而,这个类的行为被证明有点奇怪:似乎只使用了一个核心。

在网上搜索时,我在网上找到了一个很好的答案(参见Swingworker instances not running concurrently,由用户268396回答),除了问题的原因外,还提到了一个可能的解决方案:

  

你可以做些什么来解决这个问题,使用ExecutorService并发布   FutureTasks就可以了。这些将提供99%的SwingWorker API   (SwingWorker是FutureTask派生物),您只需设置即可   你的执行官。

作为一名Java初学者,我并不完全确定如何正确地做到这一点。不仅我需要将一些初始数据传递给FutureTask对象,我还需要像使用SwingWorker一样得到结果。因此,非常感谢任何示例代码。

NVX

====================编辑====================

在实施FutureTask that implements Callable中提到的简单而优雅的解决方案之后,出现了另一个问题。如果我使用ExecutorService创建单个线程,如何在线程完成运行后执行特定代码?

我试图覆盖FutureTask对象的done()(参见下面的代码),但我想应该在应用程序中完成“显示结果”位(或任何GUI相关的东西)事件派遣线程(EDT)。因此:如何将runnable提交给EDT?

package multicoretest;

import java.util.concurrent.*;

public class MultiCoreTest {

    static int coresToBeUsed = 4;
    static Future[] futures = new Future[coresToBeUsed];

    public static void main(String[] args) {
        ExecutorService execSvc = Executors.newFixedThreadPool(coresToBeUsed);
        for (int i = 0; i < coresToBeUsed; i++) {
            futures[i] = execSvc.submit(new Worker(i));
        }
        execSvc.shutdown();

        // I do not want to block the thread (so that users can
        // e.g. terminate the computation via GUI)
        //execSvc.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);

    }

    static class Worker implements Callable<String> {

        private final FutureTask<String> futureTask;
        private final int workerIdx;

        public Worker(int idx) {
            workerIdx = idx;
            futureTask = new FutureTask<String>(this) {
                @Override
                protected void done() {
                    Runnable r = new Runnable() {
                        @Override
                        public void run() {
                            showResults(workerIdx);
                        }
                    };

                    r.run(); // Does not work => how do I submit the runnable
                             // to the application's event dispatch thread?

                }
            };
        }

        @Override
        public String call() throws Exception {
            String s = "";
            for (int i = 0; i < 2e4; i++) {
                s += String.valueOf(i) + " ";
            }
            return s;
        }

        final String get() throws InterruptedException, ExecutionException {
            return futureTask.get();
        }

        void showResults(int idx) {
            try {
                System.out.println("Worker " + idx + ":" +
                        (String)futures[idx].get());
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
        }
    }
}

2 个答案:

答案 0 :(得分:2)

  

我尝试使用SwingWorker类将负载分散到   多个CPU核心。然而,这个类的行为被证明是   有点奇怪:似乎只使用了一个核心。

答案 1 :(得分:2)

有几点:

  • 您很少需要直接使用FutureTask,只需实现Callable或Runnable并将实例提交给Executor
  • 为了在完成后更新gui,作为run() / call()方法的最后一步,请使用SwingUtilities.invokeLater()代码来更新ui。

注意,您仍然可以使用SwingWorker,而不是调用execute(),而是将SwingWorker提交给您的执行者。

如果你需要在更新gui之前完成所有线程的同时处理所有结果,那么我建议:

  • 让每个工作人员将其结果存入线程安全的共享列表
  • 最后工作人员将结果添加到列表中然后应该进行后处理工作
  • 进行后处理工作的工作人员应该使用最终结果调用SwingUtilities.invokeLater()