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

Java ExecutorService invokeAny: return one successful result from competing tasks

Last updated: 5 Oct 20265 min read
tutorial
AdvancedBy AITrove Editorial

ExecutorService.invokeAny returns one successfully completed Callable result and cancels unfinished tasks when it returns.

Operational contract

A bounded set of replica lookups can race under a three-second deadline. Failure of one Callable does not prevent another from succeeding. If all fail, invokeAny raises ExecutionException; if no success arrives before the deadline, it raises TimeoutException. The caller owns the executor and its queue limits. Cancellation of losers requests interruption but does not guarantee their network or database work stopped. Use only for safe duplicate reads or operations with explicit idempotency.

Failure case

The east receipt replica throws, and the west replica returns a record. invokeAny returns west's value. If both throw, it raises an error instead of returning null, and unfinished tasks receive cancellation requests.

Java code

Java
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class ReceiptReplicaSelection {
    public static String read(ExecutorService executor,
            List<Callable<String>> replicaReads)
            throws InterruptedException, ExecutionException, TimeoutException {
        Objects.requireNonNull(executor);
        Objects.requireNonNull(replicaReads);
        if (replicaReads.isEmpty() || replicaReads.size() > 3)
            throw new IllegalArgumentException("Expected one to three replicas");
        return executor.invokeAny(replicaReads, 3, TimeUnit.SECONDS);
    }
}

Performance and ownership cost

Submitting R calls uses O(R) queue and task bookkeeping, with R capped at three. Concurrent reads may reduce latency but multiply backend load. The timeout bounds waiting for a successful result, not guaranteed completion of cancelled loser cleanup.

Common Mistakes

  • Do not confuse the first finished task with the first successful task.
  • Do not use competing non-idempotent writes as replicas.
  • Do not assume a timeout means every backend task has stopped.

Connected lessons

java
bulk executor calls
executor-invokeany-first-success
Storage details