I have an object with a method named StartDownload()
, that starts three threads.
How do I get a notification when each thread has finished executing?
Look at the Java documentation for the Thread class. You can check the thread's state. If you put the three threads in member variables, then all three threads can read each other's states.
You have to be a bit careful, though, because you can cause race conditions between the threads. Just try to avoid complicated logic based on the state of the other threads. Definitely avoid multiple threads writing to the same variables.
You should really prefer a solution that uses java.util.concurrent
. Find and read Josh Bloch and/or Brian Goetz on the topic.
If you are not using java.util.concurrent.*
and are taking responsibility for using Threads directly, then you should probably use join()
to know when a thread is done. Here is a super simple Callback mechanism. First extend the Runnable
interface to have a callback:
public interface CallbackRunnable extends Runnable {
public void callback();
}
Then make an Executor that will execute your runnable and call you back when it is done.
public class CallbackExecutor implements Executor {
@Override
public void execute(final Runnable r) {
final Thread runner = new Thread(r);
runner.start();
if ( r instanceof CallbackRunnable ) {
// create a thread to perform the callback
Thread callerbacker = new Thread(new Runnable() {
@Override
public void run() {
try {
// block until the running thread is done
runner.join();
((CallbackRunnable)r).callback();
}
catch ( InterruptedException e ) {
// someone doesn't want us running. ok, maybe we give up.
}
}
});
callerbacker.start();
}
}
}
The other sort-of obvious thing to add to your CallbackRunnable
interface is a means to handle any exceptions, so maybe put a public void uncaughtException(Throwable e);
line in there and in your executor, install a Thread.UncaughtExceptionHandler to send you to that interface method.
But doing all that really starts to smell like java.util.concurrent.Callable
. You should really look at using java.util.concurrent
if your project permits it.
I would suggest looking at the javadoc for Thread class.
You have multiple mechanisms for thread manipulation.
Your main thread could join()
the three threads serially, and would then not proceed until all three are done.
Poll the thread state of the spawned threads at intervals.
Put all of the spawned threads into a separate ThreadGroup
and poll the activeCount()
on the ThreadGroup
and wait for it to get to 0.
Setup a custom callback or listener type of interface for inter-thread communication.
I'm sure there are plenty of other ways I'm still missing.
Here's a solution that is simple, short, easy to understand, and works perfectly for me. I needed to draw to the screen when another thread ends; but couldn't because the main thread has control of the screen. So:
(1) I created the global variable: boolean end1 = false;
The thread sets it to true when ending. That is picked up in the mainthread by "postDelayed" loop, where it is responded to.
(2) My thread contains:
void myThread() {
end1 = false;
new CountDownTimer(((60000, 1000) { // milliseconds for onFinish, onTick
public void onFinish()
{
// do stuff here once at end of time.
end1 = true; // signal that the thread has ended.
}
public void onTick(long millisUntilFinished)
{
// do stuff here repeatedly.
}
}.start();
}
(3) Fortunately, "postDelayed" runs in the main thread, so that's where in check the other thread once each second. When the other thread ends, this can begin whatever we want to do next.
Handler h1 = new Handler();
private void checkThread() {
h1.postDelayed(new Runnable() {
public void run() {
if (end1)
// resond to the second thread ending here.
else
h1.postDelayed(this, 1000);
}
}, 1000);
}
(4) Finally, start the whole thing running somewhere in your code by calling:
void startThread()
{
myThread();
checkThread();
}
I guess the easiest way is to use ThreadPoolExecutor
class.
Hook methods
This class provides protected overridable
beforeExecute(java.lang.Thread, java.lang.Runnable)
andafterExecute(java.lang.Runnable, java.lang.Throwable)
methods that are called before and after execution of each task. These can be used to manipulate the execution environment; for example, reinitializing ThreadLocals, gathering statistics, or adding log entries. Additionally, methodterminated()
can be overridden to perform any special processing that needs to be done once the Executor has fully terminated.
which is exactly what we need. We will override afterExecute()
to get callbacks after each thread is done and will override terminated()
to know when all threads are done.
So here is what you should do
Create an executor:
private ThreadPoolExecutor executor;
private int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
private void initExecutor() {
executor = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2, //core pool size
NUMBER_OF_CORES * 2, //max pool size
60L, //keep aive time
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>()
) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
//Yet another thread is finished:
informUiAboutProgress(executor.getCompletedTaskCount(), listOfUrisToProcess.size());
}
}
};
@Override
protected void terminated() {
super.terminated();
informUiThatWeAreDone();
}
}
And start your threads:
private void startTheWork(){
for (Uri uri : listOfUrisToProcess) {
executor.execute(new Runnable() {
@Override
public void run() {
doSomeHeavyWork(uri);
}
});
}
executor.shutdown(); //call it when you won't add jobs anymore
}
Inside method informUiThatWeAreDone();
do whatever you need to do when all threads are done, for example, update UI.
NOTE: Don't forget about using synchronized
methods since you do your work in parallel and BE VERY CAUTIOUS if you decide to call synchronized
method from another synchronized
method! This often leads to deadlocks
Hope this helps!
Many things have been changed in last 6 years on multi-threading front.
Instead of using join()
and lock API, you can use
1.ExecutorService invokeAll()
API
Executes the given tasks, returning a list of Futures holding their status and results when all complete.
2.CountDownLatch
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
A
CountDownLatch
is initialized with a given count. The await methods block until the current count reaches zero due to invocations of thecountDown()
method, after which all waiting threads are released and any subsequent invocations of await return immediately. This is a one-shot phenomenon -- the count cannot be reset. If you need a version that resets the count, consider using a CyclicBarrier.
3.ForkJoinPool or newWorkStealingPool()
in Executors is other way
4.Iterate through all Future
tasks from submit on ExecutorService
and check the status with blocking call get()
on Future
object
Have a look at related SE questions:
How to wait for a thread that spawns it's own thread?
Executors: How to synchronously wait until all tasks have finished if tasks are created recursively?