在编写大量计算应用程序时,我尝试利用SwingWorker类将负载分散到多个CPU内核。但是,该类的行为被证明有些奇怪:似乎只使用了一个核心。
在互联网上搜索时,我在此网络上找到了一个很好的答案(请参阅Swingworker instances not running concurrently,由user268396回答)-除了引起问题的原因外,还提到了可能的解决方案:
为了解决这个问题,您可以使用ExecutorService并发布
FutureTasks就可以了。这些将提供99%的SwingWorker API
(SwingWorker是FutureTask的派生),您要做的就是设置
您的执行器正确。
作为Java初学者,我不太确定如何正确执行此操作。我不仅需要将一些初始数据传递给FutureTask对象,而且还需要像使用SwingWorker一样返回结果。因此,将非常感谢任何示例代码。
nvx
===================编辑====================
在实施FutureTask that implements Callable中提到的简单而优雅的解决方案之后,又出现了另一个问题。如果使用ExecutorService
创建单个线程,那么在线程运行完毕后如何执行特定代码?
我试图覆盖FutureTask
对象的done()(请参见下面的代码),但我想应该在应用程序的事件分发线程(EDT)中完成“显示结果”位(或任何与此GUI相关的工作) )。因此:如何将可运行文件提交给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());
}
}
}
}
最佳答案
要点:
您很少需要直接使用FutureTask,只需实现Callable或Runnable并将实例提交给Executor
为了在完成后更新gui,在run()
/ call()
方法的最后一步,将SwingUtilities.invokeLater()
与代码一起使用以更新ui。
注意,您仍然可以使用SwingWorker
,只是将SwingWorker提交给您的执行程序,而不是调用execute()
。
如果在更新gui之前完成所有线程时需要一起处理所有结果,那么我建议:
让每个工作人员将其结果存储到线程安全的共享列表中
最后将结果添加到列表的工作人员应进行后期处理工作
然后进行后处理工作的工作人员应使用最终结果调用SwingUtilities.invokeLater()
关于java - 如何正确扩展FutureTask,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15112836/