2

我有一个 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();
  }

}

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

4

2 回答 2

0

您正在从事件分派线程以外的线程访问 Swing 组件。这是Swing 线程策略所禁止的。

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

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

官方 swing 教程中阅读更多关于 swing 并发的信息。

于 2013-05-16T22:26:56.857 回答
0

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

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

执行给定的任务,返回一个 Futures 列表,在所有完成时保存它们的状态和结果。

当全部完成时”是导致进度条行为的原因。换句话说,如果您Future仅在所有任务完成时获得列表,那么显然,迭代它们并更新栏会非常快,以至于您只能看到最后一次更新,当栏已满时。

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


下面的示例代码已经过测试,可以在这里使用。您应该能够根据自己的目的对其进行调整。

监听接口:

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);
        }
    }
}

界面类:

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();
    }
}
于 2013-05-16T22:41:33.547 回答