【问题标题】:SwingWorker thread reusabilitySwingWorker 线程可重用性
【发布时间】:2013-09-06 14:03:16
【问题描述】:

我想知道 java SwingWorker 及其线程池在重复执行某些任务时如何工作。这是准备复制+粘贴的问题的SSCCE:

    package com.cgi.havrlantr.swingworkerexample;

    import java.awt.*;
    import javax.swing.*;

    public class Main extends JFrame {

        public static void main(String[] args) {
            java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                    new Main().setVisible(true);
                }
            });
        }

        public Main() {
            setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
            setSize(new Dimension(100,100));
            JButton btn = new JButton("Say Hello");
            add(btn);
            btn.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    btnPressed(evt);
                }
            });
        }

        private void btnPressed(AWTEvent e) {
            SayHelloSwingWorker worker = new SayHelloSwingWorker();
            worker.execute();
        }

        private class SayHelloSwingWorker extends SwingWorker<Integer, Integer> {

            protected Integer doInBackground() throws Exception {
                System.out.println("Hello from thread " + Thread.currentThread().getName());
                return 0;
            }
        }

    }

我想问一下。每次我在 worker 的新实例上调用 execute() 时(按下按钮后),都会在 SwingWorker 线程池中创建一个新线程,最多创建 10 个线程。超过此限制后,线程将按我的预期重用。因为新的工人是在前一个工人完成后顺序创建的,我不明白,为什么第一个线程没有立即重用,因为前一个工人已经完成了它的工作。假设只能有一个线程,它在当时做一些工作。我希望线程池只创建一个线程,这足以服务所有任务。这是通常的行为吗?或者可能有什么问题拒绝了第一个线程的可重用性并强制线程池创建另一个线程池?

如果这是正常的,我认为创建不需要的线程和保持线程就绪的内存是浪费时间。我能以某种方式避免它吗?我可以强制 SwingWorker 在他的池中只有一个线程吗? - 我认为不是,因为据我所知,线程数是恒定的并且取决于 Java 实现。

我可以让 SwingWorker 在任务完成后完成线程吗? (在 done() 方法中调用 this.cancel() 不起作用)

这是我的真实世界工人的代码,以防万一有一些可能导致问题的依赖。

public class MapWorker extends SwingWorker<Long, Object> {
    @Override
    protected Long doInBackground() throws Exception{
        try {
                long requestId = handleAction();
                return requestId;
    }catch(Exception e){
        logger.error( "Exception when running map worker thread.", e);
        SwingUtilities.invokeLater(new Runnable(){
            @Override
            public void run(){
                requestFinished();
            }
        });
        MapUtils.showFatalError(e);
    }

    return -1l;
    }

    @Override
    protected void done(){
        try{
            requestId  = get();
            logger.info("Returned from map request call, ID: {}", Long.toString(requestId));
        }catch(InterruptedException e){
            logger.error( "Done interrupted - wierd!", e);
        }catch(ExecutionException e){
            logger.error( "Exception in execution of worker thread.", e);
        }
    }

在方法 handleAction() 中创建了一个带有一些阻塞调用的新线程并返回了线程 id,这不应该有什么奇怪的。不要问我为什么,这不是我的代码。 :)

【问题讨论】:

  • 为了获得更好的帮助,请尽快发布 SSCCE,
  • Profile 验证预期;不需要invokeLater()done() 在 EDT 上运行。
  • 我认为您的链接没有显示同样的问题。

标签: java multithreading swing swingworker


【解决方案1】:

嗯... SwingWorkers 的默认 ThreadPoolExecutor 不仅配置为最大池大小为 10,而且核心大小为 10,这意味着它更愿意保持 10 个线程处于活动状态。我很难说出为什么会这样,也许它在某些条件下是最佳的。

您可以通过这个(奇怪的)调用告诉 SwingWorkers 使用自定义的 ExecutorService:

AppContext.getAppContext().put( SwingWorker.class, Executors.newSingleThreadExecutor() );

请注意,Executors.newSingleThreadExecutor() 返回的 ExecutorService 将创建一个非守护线程,您可能需要更改一些内容。

【讨论】:

  • 另外,AppContext 属于sun.awt 包,这意味着您在使用它时应该格外小心。除此之外,这似乎是唯一的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多