I would, personally, use the Executor
API.
Basically, I would create a fixed pooled executor and simply submit the tasks you want to execute to it and allow it to manage the pool of tasks as an existing thread becomes available.
Take a look at Executors tutorial for more details