ExecutorService InvokeAll进度条

时间:2013-05-16 21:44:33

标签: java concurrency

我有一个ExectorService和以下代码,一切都与期货和并发工作正常。但是,当我尝试从SomeClass()更新我的进度条时,它似乎只在invokeAll()完成后更新UI ...基本上进度条只在一切都完成后才更新,认为它没用。

我该如何解决这个问题?我看过CompletionServices以及SwingWorkers,但我不知道如何将它们应用到我的代码中。任何帮助将不胜感激。

class SomeClass() {
  private static class Result {
    private final String someVar;

    public Result(String code) {
        this.someVar = code;
    }
  }

 public static Result compute(Object obj) {
   // ... compute stuff
   someVar = "computedResult";
   return Result(someVar);
 }
 public someFunction() {
    List<Callable<Result>> tasks = new ArrayList<Callable<Result>>();
    for (Object f : listOfObjects) {
        Callable<Result> c = new Callable<Result>() {
            @Override
            public Result call() throws Exception {
                someClassUI.jProgressBar.setValue(50);
                return compute(file);
            }
        };
        tasks.add(c); 
    }

    List<Callable<Result>> tasks = new ArrayList<Callable<Result>>();
    List<Future<Result>> results = executorService.invokeAll(tasks);

    for (Future<Result> fr : results) {
     String value = fr.get().resultValue;
    }
  }
}

class SomeClassUI {
  public static jProgressBar;

  public someClassUI() {
    jProgressBar = new JProgressBar(0,100);
  }

  private void button1ActionPerformed(ActionEvent e) {
    SomeClass theClass = new SomeClass();
    theClass.someFunction();
  }

}

编辑:已修改以添加一些额外的代码以帮助理解

2 个答案:

答案 0 :(得分:0)

您正在从事件派发线程以外的线程访问Swing组件。 Swing threading policy禁止这样做。

使用此代码从后台线程更新进度条:

SwingUtilities.invokeLater(new Runnable() {
    @Override
    public void run() {
        someClassUI.jProgressBar.setValue(50);
    }
});

official swing tutorial中阅读有关摇摆中并发性的更多信息。

答案 1 :(得分:0)

我一直在使用类似于你的代码进行测试,直到我意识到以下内容:

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
                      throws InterruptedException
  

Executes the given tasks, returning a list of Futures holding their status and results when all complete.

“当所有完成”是造成进度条行为的原因。换句话说,如果只有在所有任务完成后才获得Future的列表,那么显然,迭代它们并更新条形图会很快,以至于当条形图满时,您只能看到最后一次更新。

您可以像我一样做的是为每个任务调用submit,并将Future单独添加到列表中。


以下示例代码已经过测试,可在此处使用。你应该能够根据自己的目的进行调整。

监听器界面:

public interface UpdateListener {
    void update(double percent);
}

任务执行者:

public class SomeClass {
    // instance variables
    private UpdateListener listener;
    private ExecutorService executor;

    /** Parameter constructor of objects of class SomeClass. */
    public SomeClass(UpdateListener l) {
        listener = l;
        executor = Executors.newFixedThreadPool(4);
    }

    /** */
    public void doIt() throws InterruptedException, ExecutionException {
        int numOfTasks = 5, completedTasks = 0;

        List<Future<Integer>> results = new ArrayList<>();

        // Submit each of your tasks. Here I create them manually.
        for (int i = 0; i < numOfTasks; ++i) {
            final int I = i;
            Callable<Integer> c = new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    try {
                        Thread.sleep((long) I * 1000);
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                    return new Integer(I);
                }
            };
            results.add(executor.submit(c)); 
        }

        // Retrieve individual results and update progress bar.
        for (Future<Integer> fr : results) {
            Integer i = fr.get();
            ++completedTasks;
            listener.update((double) completedTasks / numOfTasks);
        }
    }
}

UI类:

public class SomeClassUI implements Runnable, UpdateListener {
    // instance variables
    private JProgressBar bar;
    private JFrame frame;
    private SomeClass t;

    /** Empty constructor of objects of class SomeClassUI. */
    public SomeClassUI() {
        t = new SomeClass(this);
    }

    /** Builds the interface. */
    public void run() {
        bar = new JProgressBar(0, 100);
        bar.setStringPainted(true);

        JPanel panel = new JPanel(new FlowLayout());
        panel.setPreferredSize(new Dimension(200, 100));
        panel.add(bar);

        frame = new JFrame("Testing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(panel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    /** Method from the interface. Updates the progress bar. */
    @Overrides
    public void update(double percent) {
        final double PERCENT = percent;
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                int v = (int) (100 * PERCENT);
                bar.setValue(v);
            }
        });
    }

    /** Tests the program. */
    public void go() {
        SwingUtilities.invokeLater(this);
        try {
            t.doIt();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main() {
        new SomeClassUI().go();
    }
}