I use an ExecutorService to execute a task. This task can recursively create other tasks which are submitted to the same ExecutorService and those
ExecutorService
Use a Future for your tasks (instead of submitting Runnable's), a callback updates it's state when it's completed, so you can use Future.isDone to track the sate of all your tasks.
Runnable