Question

While coding a computation-heavy application, I tried to make use of the SwingWorker class to spread the load to multiple CPU cores. However, behaviour of this class proved to be somewhat strange: only one core seemed to be utilized.

When searching the internet, I found an excellent answer on this web (see Swingworker instances not running concurrently, answer by user268396) which -- in addition to the cause of the problem -- also mentions a possible solution:

What you can do to get around this is use an ExecutorService and post FutureTasks on it. These will provide 99% of the SwingWorker API (SwingWorker is a FutureTask derivative), all you have to do is set up your Executor properly.

Being a Java beginner, I am not entirely sure how to do this properly. Not only that I need to pass some initial data to the FutureTask objects, I also need to get the results back similarly as with SwingWorker. Any example code would therefore be much appreciated.

nvx

==================== EDIT ====================

After implementing the simple yet elegant solution mentioned in FutureTask that implements Callable, another issue has come up. If I use an ExecutorService to create individual threads, how do I execute specific code after a thread finished running?

I tried to override done() of the FutureTask object (see the code below) but I guess that the "show results" bit (or any GUI related stuff for that matter) should be done in the application's event dispatch thread (EDT). Therefore: how do I submit the runnable to the EDT?

package multicoretest;

import java.util.concurrent.*;

public class MultiCoreTest {

    static int coresToBeUsed = 4;
    static Future[] futures = new Future[coresToBeUsed];

    public static void main(String[] args) {
        ExecutorService execSvc = Executors.newFixedThreadPool(coresToBeUsed);
        for (int i = 0; i < coresToBeUsed; i++) {
            futures[i] = execSvc.submit(new Worker(i));
        }
        execSvc.shutdown();

        // I do not want to block the thread (so that users can
        // e.g. terminate the computation via GUI)
        //execSvc.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);

    }

    static class Worker implements Callable<String> {

        private final FutureTask<String> futureTask;
        private final int workerIdx;

        public Worker(int idx) {
            workerIdx = idx;
            futureTask = new FutureTask<String>(this) {
                @Override
                protected void done() {
                    Runnable r = new Runnable() {
                        @Override
                        public void run() {
                            showResults(workerIdx);
                        }
                    };

                    r.run(); // Does not work => how do I submit the runnable
                             // to the application's event dispatch thread?

                }
            };
        }

        @Override
        public String call() throws Exception {
            String s = "";
            for (int i = 0; i < 2e4; i++) {
                s += String.valueOf(i) + " ";
            }
            return s;
        }

        final String get() throws InterruptedException, ExecutionException {
            return futureTask.get();
        }

        void showResults(int idx) {
            try {
                System.out.println("Worker " + idx + ":" +
                        (String)futures[idx].get());
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
        }
    }
}
Was it helpful?

Solution

A couple of points:

  • you rarely need to use FutureTask directly, just implement Callable or Runnable and submit the instance to an Executor
  • in order to update the gui when you are done, as the last step of your run()/call() method, use SwingUtilities.invokeLater() with the code to update the ui.

Note, you can still use SwingWorker, just, instead of calling execute(), submit the SwingWorker to your Executor instead.

if you need to process all results together when all threads are done before updating the gui, then i would suggest:

  • have each worker stash it's results into a thread-safe, shared list
  • the last worker to add results to the list should then do the post-processing work
  • the worker which did the post-processing work should then invoke SwingUtilities.invokeLater() with the final results

OTHER TIPS

I tried to make use of the SwingWorker class to spread the load to multiple CPU cores. However, behaviour of this class proved to be somewhat strange: only one core seemed to be utilized.

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