A CountDownLatch releases waiting threads when its count reaches zero and cannot be reset for another batch.
Java CountDownLatch: completion counting does not count successful work
This complete program targets Java 8. Its displayed output is checked by the tutorial validation script.
Release the obligation on every exit
The import batch has two worker obligations. One succeeds and increments accepted; the other fails and records failure. Both decrement the latch in finally. The coordinator can then report one accepted record and one failed record without waiting forever for the failed worker.
The latch count records completion obligations, not business success. A loop that decrements only after a successful operation can hang when an exception exits the worker. Keep a separate result or failure channel so reaching zero cannot be mistaken for a successful import.
Bound the wait in the actual service
This program starts two small local threads and joins them after the latch completes. A real import needs a deadline and an interruption policy for stuck I/O. A timed-out coordinator cannot assume the workers stopped or their committed effects disappeared.
Reuse requires a new latch for a new batch. If participants change across several phases, Phaser may describe the coordination better. Either primitive still needs an explicit cancellation contract around tasks and external effects.
Working program
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class ImportCompletionCount {
public static void main(String[] args) throws Exception {
CountDownLatch completed=new CountDownLatch(2);
AtomicInteger accepted=new AtomicInteger(),failed=new AtomicInteger();
Thread first=new Thread(()->{try{accepted.incrementAndGet();}finally{completed.countDown();}});
Thread second=new Thread(()->{try{throw new IllegalStateException("bad record");}
catch(IllegalStateException failure){failed.incrementAndGet();}finally{completed.countDown();}});
first.start();second.start();completed.await();first.join();second.join();
System.out.println("accepted="+accepted.get());System.out.println("failed="+failed.get());
System.out.println("remaining="+completed.getCount());
}
}Output
accepted=1
failed=1
remaining=0Costs and boundaries
The latch holds bounded coordination state while the fixture creates two threads and two counters. Waiting duration depends on worker completion; the latch supplies no deadline on its own. Retaining all individual failures would add storage proportional to the batch size.
Common Mistakes
- Count down in finally for every registered completion obligation.
- Keep success and failure evidence separately.
- Do not reuse a completed latch for a second batch.
Read next
Java Phaser: deregister workers so a phase can complete, Java cancellation: timed waits and cooperative interruption, Spring task executors: capacity, rejection and lost context.
