I have an unbounded queue of jobs which can be processed asynchronously. The processing of each job may or may not trigger the creation of new jobs for this queue.
I would like a pool of several worker threads to take items from this queue and process them in parallel, until both the queue is empty and all worker threads are idle waiting for new jobs on the queue (as a busy worker could end up adding new jobs to the queue).
Is there a recipe for using the java.util.concurrent implementations which I can use to solve this particular problem, where workers are also producers? It is not clear that such a scenario is supported in a straightforward manner from the APIs.
In particular, I want to be able to detect the termination condition, namely, when no more jobs are available (empty job queue) and there will be no more jobs produced (all idle worker threads).
EDIT
Nam San’s answer below appears to be the most elegant approach, which basically boiled down to tracking the number of submitted jobs vs. the number of completed jobs, and using the case where these numbers were equal as the termination condition.
I’ve implemented a full example using java.util.concurrent implementations which extends ThreadPoolExecutor to achieve this, plus specialises the job queue to accept Comparable instances which are sorted in a particular way.
- TestExecutor.java: A custom executor which extends
ThreadPoolExecutorbut has additional methods to execute jobs which may create new jobs, and a new await method which waits until all submitted jobs are complete. - WorkUnit.java: An example of a comparable, runnable job which may create new jobs to submit to
TestExecutor. - Test.java: Contains a main method to run an example using
WorkUnitinstances with aTestExecutor.
The code below demonstrates how you could use a wrapper class around an
Executorto count the number of submitted jobs and compare it to the number of completed jobs to achieve what you want. Note that your tasks must call theexecutemethod of the wrapper class and never call the underlyingExecutordirectly. It should be trivial to extend the wrapper below to wrap the ‘submit’ methods of anExecutorServiceif needed.EDIT: Added test case below to demonstrate how the above code can be used