Question

I am generating SwingWorkers based on a number of connections I need to make. I am trying to make it so that I set a fixed number of maximum concurrant SwingWorkers and when one of those finishes another one is started (or many others are started if many have finished). Based on http://java.dzone.com/articles/multi-threading-java-swing I am setting up the basic SwingWorker like this:

SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
        @Override
        protected Boolean doInBackground() throws Exception {

                        System.out.println("One SwingWorker just ran! ");
                }

                return true;
        }

        // Can safely update the GUI from this method.
        protected void done() {

                boolean status;
                try {
                        // Retrieve the return value of doInBackground.
                        status = get();
                        statusLabel.setText("Completed with status: " + status);
                } catch (InterruptedException e) {
                // This is thrown if the thread's interrupted.
                } catch (ExecutionException e) {
                // This is thrown if we throw an exception
                // from doInBackground.
                }
        }


};

worker.execute();


Now I'm uncertain in how to implement the mechanism I described above.

From https://stackoverflow.com/a/8356896/988591 I saw that I can use an ExecutorService to execute instances of SwingWorker and that this interface also allows to set the number of threads:

int n = 20; // Maximum number of threads
ExecutorService threadPool = Executors.newFixedThreadPool(n);
SwingWorker w; //don*t forget to initialize
threadPool.submit(w);

I think this is what I need but I don't know how to put the whole thing together (..I am also quite new to Java..). Could someone guide me a bit in the process of implementing this? Say at the top I have int totalTask = 100; Maybe it's just a matter of some loops but I can't seem to find any really easy-to-follow examples around and I just can't totally wrap my mind around it yet so.. I would appreciate some help! Thanks.


UPDATE: I have set up the ExecutorService this way:

ExecutorService executorService = Executors.newFixedThreadPool(500);

for (int i = 0; i < 20 ; i++) {

    executorService.submit(worker); 
    //I tried both...
    //executorService.execute(worker);
}


and I have removed worker.execute() called after the SwingWorker above but the output from console is just a single "One SwingWorker just ran!" line, how is that ? What did I do wrong?

Was it helpful?

Solution 4

That moment when you think: It was so obvious!

ExecutorService executorService = Executors.newFixedThreadPool(20);

for (int i = 0; i < 500; i++) {

        SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
                @Override
                protected Boolean doInBackground() throws Exception {

                        System.out.println("One SwingWorker just ran!");
                        return true;
                }


                protected void done() {

                        boolean status;
                        try {

                                status = get();

                        } catch (InterruptedException e) {
                        // This is thrown if the thread's interrupted.
                        } catch (ExecutionException e) {
                        // This is thrown if we throw an exception
                        // from doInBackground.
                        }
                }

        };


        executorService.submit(worker);
}


It works great!

OTHER TIPS

You'd do something like this:

  • Initiate the executorservice using a fixed threadPool as you have shown.
  • In a loop create your runnable. As many runnables as you need.
  • You can have 50 threads and 5000 runnables. After the 1st 50
    runnables, whichever thread is free will pick up the 51st task, and
    so on.
  • Call the executorservice's execute method with your Runnable.
  • Once all are done, you shutdown the executor service.

Like this:

ExecutorService executorService = Executors.newFixedThreadPool(500);

for (long i = 0; i < 1000000; i++) {
    Runnable populator = new YourRunnable();
    executorService.execute(populator);
}
executorService.shutdown();
while(!executorService.isTerminated()){
}

That isTerminated can be used to check whether the executorServices is actually down. Since you can have several executor threads running even after you call the shutdown() method (because they haven't completed the task yet), that while loop acts like a wait call.

And one key thing: whatever you want to pass to the ExecutorService, it must be a Runnable implementation. In your case, your SwingWorker must be a Runnable.

That's interesting. With an ExecutorService with a limited pool, you just need to submit the workers whenever you want and only that amount of workers will be executed concurrently at the same time.

I made this little test app that where you can press the buttons to submit some workers, as fast as you want, and you can see how the amount of workers executing at any given time is never higher than the value numberOfThreads that you initialized the ExecutorService with.

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class SwingWorkerExecutorTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                new SwingWorkerExecutorTest();
            }
        });
    }


    public SwingWorkerExecutorTest()
    {
        JFrame frame = new JFrame("Frame");

        int numberOfThreads = 2; //1 so they are executed one after the other.
        final ExecutorService threadPool = Executors.newFixedThreadPool(numberOfThreads);

        JButton button1 = new JButton("Submit SwingWorker 1");
        button1.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                String workerName = "Worker 1";
                appendMessage("Submited " + workerName);
                SwingWorker worker = new TestWorker(workerName);
                threadPool.submit(worker);
            }
        });

        JButton button2 = new JButton("Submit SwingWorker 2");
        button2.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                String workerName = "Worker 2";
                appendMessage("Submited " + workerName);
                SwingWorker worker = new TestWorker(workerName);
                threadPool.submit(worker);
            }
        });

        JButton button3 = new JButton("Submit SwingWorker 3");
        button3.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                String workerName = "Worker 3";
                appendMessage("Submited " + workerName);
                SwingWorker worker = new TestWorker(workerName);
                threadPool.submit(worker);
            }
        });

        JPanel buttonsPanel = new JPanel();
        buttonsPanel.add(button1);
        buttonsPanel.add(button2);
        buttonsPanel.add(button3);
        frame.add(buttonsPanel, BorderLayout.PAGE_END);

        _textArea = new JTextArea("Submit some workers:\n");
        _textArea.setEditable(false);
        frame.add(new JScrollPane(_textArea));

        frame.setSize(600, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }


    private class TestWorker extends SwingWorker
    {
        public TestWorker(String name)
        {
            _name = name;
        }

        @Override
        protected Object doInBackground() throws Exception
        {
            String message = "A " + _name + " has started!";
            appendMessage(message);
            doHardWork();
            return null;
        }

        @Override
        protected void done()
        {
            String message = "A " + _name + " has finished!";
            appendMessage(message);
        }

        private void doHardWork()
        {
            try
            {
                Thread.sleep(2000);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }

        private String  _name;
    }

    private static void appendMessage(String message)
    {
        _textArea.append(message + "\n");
        System.out.println(message);
    }

    private static JTextArea    _textArea;
}

It looks like this:

example

For example, with a number of threads of 2 you'll se how if you submit a lot of workers it takes 2 at a time and executes them.

Please take a look at the source code of SwingWorker. You can something similar to execute() method

 public final void execute() {
    getWorkersExecutorService().execute(this);
}

At this point you can create your one ExecutorService and manage the pool

 SwingWorker<Boolean, Void> test = new SwingWorker<Boolean, Void>() {
        private ExecutorService service =  new ThreadPoolExecutor(5, 10,
                10L, TimeUnit.MINUTES,
                new LinkedBlockingQueue<Runnable>(),
                new ThreadFactory() {
                    AtomicInteger count= new AtomicInteger();
                    @Override
                    public Thread newThread(Runnable r) {
                        return new Thread("Pooled SwingWorker " + count.getAndAdd(1));
                    }
                });
        @Override
        protected Boolean doInBackground() throws Exception {
            return true;
        }

        public void doIt() {
            service.execute(this);
        }

    };
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top