Radium
Radium

Reputation: 604

SwingWorker thread reusability

I wonder how java SwingWorker and it's thread pool works when some task is performed repeteadly. Here is SSCCE of problem ready to copy + paste:

    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;
            }
        }

    }

I want to ask on following. Every time I call execute() on a new instance of worker (after button is pressed), a new thread in SwingWorker thread pool is created up to 10 threads total created. After exceeding this limit, threads are reused as I expect. Because new workers are created sequentially after the previous one is finished, I don't understand, why the first thread is not reused immeadiately, because the previous worker already done it's work. Say there can be only one single thread, which does some work at the time. I would expect thread pool to create only one thread, which is enought to serve all tasks. Is this usual behaviour? Or there may be something wrong what denies reusability of the first thread and forces thread pool to create another one?

If it is normal I think it is waste of time for creation unwanted thread and memory for keeping the threads ready. Can I avoid it somehow? Can I force SwingWorker to have only one thread in his pool? - I think no, because number of threads is constant and dependent on Java implementation as far as I know.

Can I make SwingWorker to finish the thread after task was completed? (calling this.cancel() in done() method did not work)

Here is code of my real world worker, for case there is some dependency that may cause the problem.

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);
        }
    }

In method handleAction() a new thread with some blocking call is created and thread id is returned, which should not be anything weird. Don't ask me why, it is not my code. :)

Upvotes: 3

Views: 923

Answers (1)

TrogDor
TrogDor

Reputation: 1029

Hmmm... the default ThreadPoolExecutor for SwingWorkers is configured to not only have a max pool size of 10 but also a core size of 10, meaning it prefers to keep 10 threads alive. Hard for me to tell why this is, maybe it's optimal under certain conditions.

You can tell SwingWorkers to use a custom ExecutorService using this (weird) call:

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

Note, the ExecutorService returned by Executors.newSingleThreadExecutor() will create a non-daemon thread, something you may want to change.

Upvotes: 3

Related Questions