javax.swing.SwingWorker中的问题

时间:2009-10-26 14:05:33

标签: java multithreading swingworker

我做了一个swings应用程序但是有一个问题如下:

我从Event Dispatch Thread发起了一个名为“Thread-Main”的SwingWorker线程,并将GUI的JLabel引用传递给了“Thread-Main”。

现在我已经从“Thread-Main”开始了10个线程。

现在我希望所有10个线程都应该更新JLabel。

我该怎么做?

有人告诉我,我可以通过首先创建SwingWorker的所有10个线程子类然后调用publish(“”)方法并在该“publish”方法中传递字符串然后收集所有已发布的字符串来实现此目的。 “Thread-Main”中的以下方法

@Override
protected void process(List<String> labelStrings) {
    String count = labelStrings.get(labelStrings.size() - 1);
    label.setText(count); // label is a reference to a label in the GUI 
}
  1. 上述方法是否正确?
  2. 10个线程应该是SwingWorker的子类吗?
  3. 还有其他办法吗?

3 个答案:

答案 0 :(得分:4)

也许更简单的方法是在SwingUtilities.invokeLater(...)方法中包装更新GUI的代码。

编辑:在您想要更新标签的个别线程中:

SwingUtilities.invokeLater(new Runnable()
{
    public void run()
    {
        label.setText(...);
    }
});

答案 1 :(得分:3)

  1. - 如果您想自己控制线程数,这是错误的方法。为什么?因为如果你查看SwingWorker代码,你会发现它在内部使用ThreadPoolExecutor,最多包含10个线程。如果同时启动多个SwingWorker,它们将全部使用此执行程序运行。但是,您无法直接控制后台线程是否并行执行
  2. 见第1点。
  3. 我推荐的解决方案是:

    • 创建一个SwingWorker
    • doInBackground()方法中,直接或使用ExecutorService启动10个主题。
    • 使用CountDownLatchCompletionService在主线程(即SwingWorker后台线程)和工作线程之间进行同步。
  4. 示例

    定义要调用的工作线程数,并声明要更新的JLabel。

    final int nThreads = 10;
    JLabel myLbl = new JLabel();
    

    将我们希望执行的工作单元定义为Callable<String>String结果将用于更新JLabel

    private static class MyCallable implements Callable<String> {
      public String call() { ... }
    }
    

    现在开始SwingWorker,这将启动多个并行工作人员进行任何处理。工作人员不会通过调用done()(因此Void类型)返回结果,但通过调用将中间String结果重新发送回Swing线程process(String... chunks)

    new SwingWorker<Void, String>() {
      // See method definitions below.
    }.execute();
    

    使用doInBackground()定义CompletionService以启动工作线程并阻止每个结果。

    public Void doInBackground() throws Exception {
      // Define executor service containing exactly nThreads threads.
      ExecutorService execService = Executors.newFixedThreadPool(nThreads);
    
    
      // Define completion service that will contain the processing results.
      CompletionService compService = new ExecutorCompletionService(execService);    
    
      // Submit work to thread pool using the CompletionService.  Future<String>
      // instances will be added to the completion service's internal queue until complete.
      for (int i=0; i<nThreads; ++i) {
        compService.submit(new MyCallable());
      }
    
      // Take results from each worker as they appear and publish back to Swing thread.
      String result;
      while ((result = compService.take().get()) != null) {
        publish(result);
      }
    }
    

    现在我们实施process(String... chunks)只是在调用时更新JLabel

    public void process(String... chunks) {
      if (chunks.length > 0) {
        // Update label with last value in chunks in case multiple results arrive together.
        myLbl.setText(chunks[chunks.length - 1]);
      }
    }
    

    最后,我们覆盖done()以将任何异常编组回Swing线程。

    public void done() {
      try {
        get(); // Will return null (as per Void type) but will also propagate exceptions.
      } catch(Exception ex) {
        JOptionPane.show ... // Show error in dialog.
      }
    }
    

答案 2 :(得分:0)

为什么你需要10个线程?为什么不会有一个额外的线程呢?

真正的问题:你要解决的问题是什么?

回答你的直接问题:

1)是的,这是正确的方法 2)是的,线程应该是SwingWorkers(如果你使用netbeans,你也可以使用Tasks,它们也是SwingWorker的子类)

3)如果你想从edt中获得一个单独的线程;然后你需要使用swingworker;所以方式就是这样做的。

祝你好运!