I am using a ScheduledExecutorService to schedule and process jobs across several threads. In my application, a job can schedule a new job (on the same ScheduledExecutorService), as some kind of follow-up action.
In the main thread, I want to wait until all jobs are finished, as a synchronization point. There are the shutdown() and awaitTermination() methods, but this disallows any running or pending job to schedule a new job. In my case, I actually want to allow this, accepting the risk that we will never finish (or hit some timeout).
How do I wait for all jobs and possibly their follow-up jobs to finish?
CodePudding user response:
It's possible by keeping track of the number of active jobs. Effectively, each job that you submit must be wrapped as follows (pseudo code):
increase active jobs by one
try {
run actual job
} finally {
decrease active jobs by one
}
Of course the ScheduledExecutorService needs to be fully encapsulated for this to work.
The next step is to find the right concurrent mechanism for this, that doesn't introduce any busy waiting.
A quick and dirty attempt using a Lock with a Condition that signals no more jobs:
private final Lock lock = new ReentrantLock();
private final Condition noJobs = lock.newCondition();
private long jobCount = 0;
private void increaseJobCount() {
lock.lock();
try {
jobCount ;
} finally {
lock.unlock();
}
}
private void decreaseJobCount() {
lock.lock();
try {
jobCount--;
if (jobCount == 0) {
noJobs.signalAll();
}
} finally {
lock.unlock();
}
}
public void awaitNoJobs() throws InterruptedException {
lock.lock();
try {
while (jobCount > 0) {
noJobs.await();
}
} finally {
lock.unlock();
}
}
I checked some other known concurrent classes, but apart from a BlockingQueue / BlockingDeque I couldn't find any. Those take up more memory though, as you'd have to add and remove something for each job that's submitted. A CountDownLatch comes close but doesn't allow increasing the count.
