views:

433

answers:

8

What do you think is the best way for obtaining the results of the work of a thread? Imagine a Thread wich does some calculations, how do you warn the main program the calculations are done?

You could poll every X milliseconds for some public variable called "job finished" or something by the way, but then you'll receive the results later than when they would be avaliable... the main code would be losing time waiting for them. On the other hand, if you use a lower X, the CPU would be wasted polling so many times.

So, what do you do to be aware that the Thread, or some Threads, have finished their work?

Sorry if it looks similar to this other question, that's probably the reason for the eben answer, I suppose. What I meaned was running lots of threads and know when all of them have finished, without polling them.

I was thinking more in the line of sharing the CPU load between multiple CPU's using batches of Threads, and know when a batch has finished. I suppose it can be done with Futures objects, but that blocking get method looks a lot like a hidden lock, not something I like.

Thanks everybody for your support. Althought I also liked the answer by erickson, I think saua's the most complete, and the one I'll use in my own code.

+20  A: 

Don't use low-level constructs such as threads, unless you absolutely need the power and flexibility.

You can use a ExecutorService such as the ThreadPoolExecutor to submit() Callables. This will return a Future object.

Using that Future object you can easily check if it's done and get the result (including a blocking get() if it's not yet done).

Those constructs will greatly simplify the most common threaded operations.

I'd like to clarify about the blocking get():

The idea is that you want to run some tasks (the Callables) that do some work (calculation, resource access, ...) where you don't need the result right now. You can just depend on the Executor to run your code whenever it wants (if it's a ThreadPoolExecutor then it will run whenever a free Thread is available). Then at some point in time you probably need the result of the calculation to continue. At this point you're supposed to call get(). If the task already ran at that point, then get() will just return the value immediately. If the task didn't complete, then the get() call will wait until the task is completed. This is usually desired since you can't continue without the tasks result anyway.

When you don't need the value to continue, but would like to know about it if it's already available (possibly to show something in the UI), then you can easily call isDone() and only call get() if that returns true).

Joachim Sauer
+1  A: 

Polling a.k.a busy waiting is not a good idea. As you mentioned, busy waiting wastes CPU cycles and can cause your application to appear unresponsive.

My Java is rough, but you want something like the following:

If one thread has to wait for the output of another thread you should make use of a condition variable.

final Lock lock = new ReentrantLock();
final Condition cv = lock.newCondition();

The thread interested in the output of the other threat should call cv.wait(). This will cause the current thread to block. When the worker thread is finished working, it should call cv.signal(). This will cause the blocked thread to become unblocked, allowing it to inspect the output of the worker thread.

fpsgamer
+1  A: 

This is very similar to this question.

kgiannakakis
A: 

As noted by saua: use the constructs offered by java.util.concurrent. If you're stuck with a pre 1.5 (or 5.0) JRE, you ,might resort to kind of rolling your own, but you're still better of by using a backport: http://backport-jsr166.sourceforge.net/

Jeroen van Bergen
+1  A: 

As an alternative to the concurrency API as described by Saua (and if the main thread doesn't need to know when a worker thread finishes) you could use the publish/subscribe pattern.

In this scenario the child Thread/Runnable is given a listener that knows how to process the result and which is called back to when child Thread/Runnable completes.

Nick Holt
+1  A: 

Your scenario is still a little unclear.

If you are running a batch job, you may want to use invokeAll. This will block your main thread until all the tasks are complete. There is no "busy waiting" with this approach, where the main thread would waste CPU polling the isDone method of a Future. While this method returns a list of Futures, they are already "done". (There's also an overloaded version that can timeout before completion, which might be safer to use with some tasks.) This can be a lot cleaner than trying to gather up a bunch of Future objects yourself and trying to check their status or block on their get methods individually.

If this is an interactive application, with tasks sporadically spun off to be executed in the background, using a callback as suggested by nick.holt is a great approach. Here, you use the submit a Runnable. The run method invokes the callback with the result when it's been computed. With this approach, you may discard the Future returned by submit, unless you want to be able to cancel running tasks without shutting down the whole ExecutorService.

If you want to be able to cancel tasks or use the timeout capabilities, an important thing to remember is that tasks are canceled by calling interrupt on their thread. So, your task needs to check its interrupted status periodically and abort as needed.

erickson
+1  A: 

Subclass Thread, and give your class a method that returns the result. When the method is called, if the result hasn't been created, yet, then join() with the Thread. When join() returns, your Thread's work will be done and the result should be available; return it.

Use this only if you actually need to fire off an asynchronous activity, do some work while you're waiting, and then obtain the result. Otherwise, what's the point of a Thread? You might as well just write a class that does the work and returns the result in the main thread.

Another approach would be a callback: have your constructor take an argument that implements an interface with a callback method that will be called when the result is computed. This will make the work completely asynchronous. But if you at all need to wait for the result at some point, I think you're still going to need to call join() from the main thread.

skiphoppy
+1  A: 

You could create a lister interface that the main program implements wich is called by the worker once it has finished executing it's work.

That way you do not need to poll at all.

Here is an example interface:

/**
 * Listener interface to implement to be called when work has
 * finished.
 */
public interface WorkerListener {
    public void workDone(WorkerThread thread);
}

Here is an example of the actual thread which does some work and notifies it's listeners:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Thread to perform work
 */
public class WorkerThread implements Runnable {
    private List listeners = new ArrayList();
    private List results;

    public void run() {
     // Do some long running work here

     try {
      // Sleep to simulate long running task
      Thread.sleep(5000);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }

     results = new ArrayList();
     results.add("Result 1");

     // Work done, notify listeners
     notifyListeners();
    }

    private void notifyListeners() {
     for (Iterator iter = listeners.iterator(); iter.hasNext();) {
      WorkerListener listener = (WorkerListener) iter.next();
      listener.workDone(this);
     }
    }

    public void registerWorkerListener(WorkerListener listener) {
     listeners.add(listener);
    }

    public List getResults() {
     return results;
    }
}

And finally, the main program which starts up a worker thread and registers a listener to be notified once the work is done:

import java.util.Iterator;
import java.util.List;

/**
 * Class to simulate a main program
 */
public class MainProg {
    public MainProg() {
     WorkerThread worker = new WorkerThread();
     // Register anonymous listener class
     worker.registerWorkerListener(new WorkerListener() {
      public void workDone(WorkerThread thread) {
       System.out.println("Work done");
       List results = thread.getResults();
       for (Iterator iter = results.iterator(); iter.hasNext();) {
        String result = (String) iter.next();
        System.out.println(result);
       }
      }
     });

     // Start the worker thread
     Thread thread = new Thread(worker);
     thread.start();

     System.out.println("Main program started");
    }

    public static void main(String[] args) {
     MainProg prog = new MainProg();
    }
}
Neal Donnan