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!