A runtime failure from RecursiveTask.compute propagates through join or invoke. A sibling task may already have run, so failure of the result is not an atomic rollback of side effects.
Java fork/join failures: one failed task does not roll back sibling work
Operational contract
Keep fork/join computations pure or confine mutations to disjoint owned ranges. The sample validates each shipment quantity before contributing to an aggregate. If one leaf fails, the caller receives a computation failure, but other leaves may have completed. No database or external mutation occurs, so discarding the final sum is safe. If a task writes outside its range, the caller needs separate reconciliation and cancellation; a thrown exception alone will not undo that work. Use an owned pool with a finite batch and record the original cause.
Failure case
A batch contains 47,000 counts and one negative quantity. The validation task throws instead of returning a misleading sum. Other subranges may already have been summed. That wasted computation is acceptable; writing partial totals to a ledger from each leaf would not be, because the exception cannot retract those ledger entries.
Java code
import java.util.concurrent.RecursiveTask;
public class ValidatedShipmentSum extends RecursiveTask<Long> {
private final int[] quantities;
private final int start;
private final int end;
public ValidatedShipmentSum(int[] quantities, int start, int end) {
this.quantities = quantities;
this.start = start;
this.end = end;
}
@Override protected Long compute() {
if (end - start <= 256) {
long total = 0;
for (int index = start; index < end; index++) {
if (quantities[index] < 0) throw new IllegalArgumentException("Negative quantity at " + index);
total += quantities[index];
}
return total;
}
int middle = start + (end - start) / 2;
ValidatedShipmentSum left = new ValidatedShipmentSum(quantities, start, middle);
left.fork();
long rightTotal = new ValidatedShipmentSum(quantities, middle, end).compute();
return rightTotal + left.join();
}
}Performance and ownership cost
A valid batch does O(N) validation and summation. An invalid item can reduce useful work, but parallel siblings may still complete, so the failure path has no fixed early-stop cost. The split threshold limits task count to roughly O(N/256) leaves.
Common Mistakes
- Do not assume an exception cancels sibling work immediately.
- Do not write irreversible side effects from each leaf and expect rollback.
- Do not swallow a validation exception and publish a partial total.
Connected lessons
- Java RecursiveTask: split work only above a measured threshold
- Java ForkJoinPool: own a custom pool and its shutdown
- Java cancellation: timed waits and cooperative interruption
- Java ManagedBlocker: declare a rare blocking wait to the pool
- Java parallel streams: reduce values instead of mutating shared results
- Java HTTP and fork/join decisions quiz
- Advanced Java
