CompletableFuture.thenCombine combines the normal results of two stages after both finish; it does not impose an order on independent work.
Java thenCombine: join two independent results without serializing them
Operational contract
The two futures are supplied by the caller, which can start them concurrently on a controlled executor. This method only combines their results. A failed input makes the dependent result fail, while the other input can still keep running. A null value is rejected explicitly because a missing rate cannot produce a valid quote. If one input never finishes, the combined stage needs a separate deadline policy.
Failure case
The insurance service fails while the base-rate service remains busy. The combined quote cannot succeed, but combining the futures does not cancel the base-rate request or release its resource.
Java code
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
public class InsuredShipmentQuote {
public static CompletableFuture<Long> totalCents(
CompletableFuture<Long> baseRate,
CompletableFuture<Long> insuranceRate) {
Objects.requireNonNull(baseRate);
Objects.requireNonNull(insuranceRate);
return baseRate.thenCombine(insuranceRate, (base, insurance) -> {
if (base == null || insurance == null || base < 0 || insurance < 0)
throw new IllegalArgumentException("Rates must be nonnegative");
return Math.addExact(base, insurance);
});
}
}Performance and ownership cost
The combination adds O(1) local time and space. If operations truly start concurrently, latency is near the slower input rather than their sum, plus callback overhead. Each upstream call still consumes its own resources.
Common Mistakes
- Do not start the second independent request inside a thenCompose callback.
- Do not assume thenCombine cancels the surviving input after failure.
- Do not ignore overflow when amounts are stored in integer cents.
