Concurrent task exercises must verify completed work rather than treating successful submission as successful execution.
Java concurrency exercises: completed-task accounting and worker shutdown
Java 8+. This is a complete program using JDK classes.
Write the acceptance checks first
Submit twenty tasks, each incrementing a shared counter ten times. The final count must be two hundred. Each task must complete before the counter is read, and the executor must terminate before the process is considered finished.
The exercise uses AtomicInteger for individual increments and Futures for task completion. The atomic counter alone does not tell the caller when all tasks are done. Waiting on every future also exposes task failures through ExecutionException instead of replacing them with an apparently successful final count.
This small fixed batch is deliberately bounded at its source. Executors.newFixedThreadPool uses a queue whose capacity is not a complete overload policy. A public service accepting unbounded submissions needs an explicitly bounded queue and rejection behavior, even if its worker count is small.
Working program
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class CompletedCounterExercises {
public static void main(String[] args) throws Exception {
ExecutorService workers=Executors.newFixedThreadPool(4);AtomicInteger completed=new AtomicInteger();
List<Future<?>> tasks=new ArrayList<>();
try{
for(int batch=0;batch<20;batch++)tasks.add(workers.submit(()->{
for(int item=0;item<10;item++)completed.incrementAndGet();
}));
for(Future<?> task:tasks)task.get(2,TimeUnit.SECONDS);
if(completed.get()!=200)throw new AssertionError("Lost work");
System.out.println("completed="+completed.get());
}finally{
workers.shutdownNow();
if(!workers.awaitTermination(2,TimeUnit.SECONDS))throw new AssertionError("Workers retained");
}
}
}Output
completed=200Costs and boundaries
The workload performs 200 atomic increments and stores twenty futures. Contention can affect execution time even though the total arithmetic work is fixed. These per-future waits are test guards, not one shared request deadline; a production batch needs an overall deadline policy.
Common Mistakes
- A submitted future can later fail.
- A shared int increment is not atomic.
- A fixed worker count does not by itself bound the pending queue.
