【问题标题】:Java - SwingWorker - problemJava - SwingWorker - 问题
【发布时间】:2011-02-17 02:22:05
【问题描述】:

我正在开发 Java 桌面应用程序。这个应用程序在多个线程中同时执行相同的任务public class MyTask implements Callable<MyObject> {

现在,当用户点击“开始”按钮时,我创建了一个SwingWorker myWorker 并执行了它。

现在,这个myWorker 创建多个MyTask 实例并将它们提交给ExecutorService

每个MyTask 实例都有一个循环,并在每次迭代时生成一个中间结果。现在,我想在每个MyTask 实例生成后立即收集这些中间结果。然后从每个MyTask 实例收集这些中间结果后,我想通过SwingWorker.publish(MyObject) 发布它,以便在 EDT 上显示进度。

Q1.我该如何实现呢?我是否应该将MyTask 设为SwingWorker 的子类而不是Callable 以获得中间结果,因为我认为Callable 只返回最终结果。

Q2. 如果 Q1 的答案。是的,那么你能给我一个小例子来说明我如何获得这些中间结果并聚合它们,然后从 main SwingWorker 发布它们吗?

Q3.如果在这种情况下我不能使用SwingWorker,那我该如何实现呢?

【问题讨论】:

  • 当您收集每个任务的中间结果时,您是否希望它们都来自同一个迭代? IE。收集所有迭代 1 结果,然后收集迭代 2 结果?

标签: java user-interface swingworker callable


【解决方案1】:

看看ExecutorCompletionService<T>。它是一个 Executor,提供了一个 take 方法来检索任何已完成任务的结果。

更新:

扩展SwingWorker 不会做你想做的事,因为它专门用于将工作从 EDT 卸载到后台线程。您不能使用它将工作从后台线程卸载到其他后台线程。对SwingWorker.publish 的调用会产生与SwingUtilities.invokeLater 等效的结果。我不知道从后台线程到后台线程执行相同操作的机制。最好的办法是创建您的 MyTask 并引用 Queue 并让您的 SwingWorker.doInBackground 轮询队列以获取中间结果。

【讨论】:

  • 我想从一个SwingWorker mainWorker 创建几个SwingWorker。此mainWorker 由 EDT 发起。从主要工作人员创建多个工作人员的原因是我想在mainWorker 中收集所有这些工作人员的中间结果,然后通过发布这些中间结果。 publish(...)mainWorker的方法。
【解决方案2】:

一个 SwingWorker 也是一个 Future。因此,它具有 get() 方法,可以在 done() 方法中使用该方法以在该方法完成时获取 doInBackground() 的结果。

因此构造有点像:

SwingWorker<T,P> sw=new SwingWorker<T,P>() {

  @Override
  public T doInBackground() throws Exception {
    T result;
    // do stuff here
    return result;
  }

  @Override
  public void done() {
    try {
      T result=get();
      // do stuff with result.
    }
    catch(ExecutionException e) {
      Exception fromDoInBackground= (Exception) e.getCause();
      // handle exception thrown from doInBackground()
    }
    catch(InterruptedException i) {
      // handle the case in which a SwingWorker was cancelled. typically: do nothing.
    }
  }
};

【讨论】:

  • @user 我要求您在回答之前先仔细阅读问题......我已经知道如何使用SwingWorker。 :)
【解决方案3】:

A1+A2。 Yatendra,您的主要SwingWorker 是否必须是唯一将临时结果传递给EDT 的人?如果您的任务也是 SwingWorker 实例,则 Main Worker 可以将向它们发送回 EDT 的临时结果的责任委托给它们,并只处理 TaskWorkers 生命周期。

package threading;

import java.util.LinkedList;
import java.util.List;

import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

class MainSwingWorker extends SwingWorker<Void, Void> {
    private List<TaskWorker> tasks;

    public MainSwingWorker() {
        tasks = new LinkedList<TaskWorker>();
        for(int i=0; i<2; i++) 
            tasks.add(new TaskWorker(i));
    }

    @Override
    public Void doInBackground() throws Exception {
        Test.log("Building tasks.");                    
        for(TaskWorker task : tasks) 
            launch(task);
        Test.log("Waiting 5 secs.");
        Thread.sleep(5000);

        Test.log("Cancelling tasks");

        for(TaskWorker task : tasks ) 
            task.cancel(true);

        return null;
    }

    private void launch(final TaskWorker task) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                Test.log("Launching task worker.");
                task.execute();
            }
        });     
    }
}

class TaskWorker extends SwingWorker<Void, String> {
    private int id;

    public TaskWorker(int wid) {
        id = wid;
    }

    @Override
    public Void doInBackground() throws Exception {     
        System.out.format("[%s] Starting worker %s\n", Thread.currentThread().getName(), id );
        while( !isCancelled() ) {
            // ***************************
            // your task process code here
            // ***************************
            publish(String.format("A dummy interim result #%s", id));
            Thread.sleep(1000);
        }       
        return null;
    }

    @Override
    public void process(List<String> results) {
        // it's pretty obvious, that once this method gets called you can safely 
        // call the Swing API from EDT among with the interim results
        for(String result : results )
            Test.log(result);
    }
}

public class Test {

    public static void log(String msg) {
        System.out.format("[%s] %s\n", Thread.currentThread().getName(), msg);
    }

    public static void main(String[] args) throws Exception {
        log("Init.");
        SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                log("Starting main worker.");
                MainSwingWorker worker = new MainSwingWorker();
                worker.execute();                           
            }
        });
        Thread.sleep(7000);
        log("Finished.");
    }
}

请记住,这只是一个测试,我知道有一些丑陋的Thread.sleep(long) 调用。

[main] Init.
[AWT-EventQueue-0] Starting main worker.
[SwingWorker-pool-1-thread-1] Building tasks.
[SwingWorker-pool-1-thread-1] Waiting 5 secs.
[AWT-EventQueue-0] Launching task worker.
[AWT-EventQueue-0] Launching task worker.
[SwingWorker-pool-1-thread-2] Starting worker 0
[SwingWorker-pool-1-thread-3] Starting worker 1
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #1
[SwingWorker-pool-1-thread-1] Cancelling tasks
[main] Finished.

A3 但是,如果您的项目需要另一个ExecutorService 来安排您的任务执行,那么我将实现一个类似的发布流程机制来执行您的 Main Swing 工作线程和该任务线程之间的通信。虽然它似乎是重复的,但您可以使用java.concurrent.ConcurrentQueue 来存储可用的临时结果吗?

PS:我几天前才注意到,但是 SwingWorkers 周围有个烦人的 bug,prevents its ExecutorService from caching unused threads

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多