3

我想知道当重复执行某些任务时,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,这不应该是什么奇怪的事情。不要问我为什么,这不是我的代码。:)

4

1 回答 1

3

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

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

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

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

于 2013-09-06T17:43:11.157 回答