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.