我有一个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();
}
}
编辑:已修改以添加一些额外的代码以帮助理解
答案 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
“当所有完成”是造成进度条行为的原因。换句话说,如果只有在所有任务完成后才获得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();
}
}