Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java CountDownLatch: completion counting does not count successful work

Last updated: 29 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

A CountDownLatch releases waiting threads when its count reaches zero and cannot be reset for another batch.

Download Java source kit

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

Java
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

Output
accepted=1
failed=1
remaining=0

Costs 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.

java
countdown-latch
Storage details