3

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?

Community
  • 1
  • 1
Redoman
  • 3,059
  • 3
  • 34
  • 62
  • 1
    as I noticed in your previous question, carefully with number of SwingWorkers instances are concurently running, SwingWorker is intialized from Future and nothing guarantee that every instances ended in the same time or beforr another instance is started (valid for logics with Thread too, but without bugs), logics with Runnable#Thead is better, easier and confortable, get() in done() to use for catch for an possible exception, not for returns – mKorbel Apr 30 '14 at 23:13
  • I'm not interested in the order they finish in this case. Not sure what you mean with "but without bugs". In all documentation I read for SwingWorker done() and get() they are used respectively to issue a callback method which 1) can access the return value of the SwingWorker doInBackground() and 2) can safely update the GUI (as it is automatically launched in the EDT). I am not sure if you refer to other get() and done() methods and I don't know if they are also used in Runnable#Thread. – Redoman May 01 '14 at 07:07
  • 1. you can to [get() an exception or returns](http://stackoverflow.com/questions/7053865/cant-get-arrayindexoutofboundsexception-from-future-and-swingworker-if-threa) 2. publish()/setProcess – mKorbel May 01 '14 at 19:34
  • hey... i experienced that same here. But the all of my swingworkers are fine they're completing the job good. Only the strange part is the done() Method called just once, just like you've experienced. – gumuruh Oct 21 '20 at 13:45

4 Answers4

1

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.

Praba
  • 1,373
  • 10
  • 30
  • So.. uhm.. how to make the SwingWorker into a Runnable? – Redoman Apr 30 '14 at 18:00
  • I shouldn't be doing this but a bit of Googling returned me this: http://greatratrace.blogspot.in/2010/01/throttling-swingworker-using.html . You can adapt this to suit your need. – Praba Apr 30 '14 at 18:05
  • I actually had visited that page before, but I closed it because of the shitty layout. Thanks for making me reconsider it. – Redoman Apr 30 '14 at 18:14
  • You don't let something as irritating as a layout stop you from getting what you need. :) – Praba Apr 30 '14 at 18:17
  • I know but add that to a 6-days-old experience with Java and you get the picture.. – Redoman Apr 30 '14 at 18:19
  • Sorry but probably due to my inexperience, I fail to see the connection between the example code you wrote above (where I just needed to make my SwingWorker into a runnable) with the page you linked, which seems to use a different approach and so I don't see how am I supposed to understand how to adapt that to my code. Also I'd like to keep my code as simple as possible, and first approach looked easier. Can you expand on that approach? – Redoman Apr 30 '14 at 18:45
  • My bad, I didn't realize SwingWorker already implements Runnable. So nothing extra needed here. The code I have given shows how executor service is used in general. The link shows how to use a swingworker with an executorservice. All you had to do was to create the executor service and pass your SwingWorker to it. – Praba Apr 30 '14 at 18:51
1

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.

DSquare
  • 2,458
  • 17
  • 19
0

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

    };
YaRiK
  • 698
  • 6
  • 13
0

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!

Redoman
  • 3,059
  • 3
  • 34
  • 62