Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01B38FGKKam5SCGgwgduVAh3
103 lines
4.7 KiB
Java
103 lines
4.7 KiB
Java
import java.time.Duration;
|
|
import java.util.List;
|
|
import java.util.concurrent.ExecutionException;
|
|
import java.util.concurrent.StructuredTaskScope;
|
|
import java.util.concurrent.StructuredTaskScope.Joiner;
|
|
import java.util.concurrent.StructuredTaskScope.Subtask;
|
|
|
|
/**
|
|
* JEP 533, Structured Concurrency (seventh preview), written against the JDK 27 API.
|
|
*
|
|
* <p>The headline change from JDK 26 is that {@code StructuredTaskScope} and {@code Joiner} gained a
|
|
* third type parameter, {@code R_X}: the exception {@code join()} throws. That lets a scope throw YOUR
|
|
* exception type rather than a generic {@code FailedException}. Compare broken/StructuredScope26.java,
|
|
* which is JDK 26 code that no longer compiles. Explained in docs/07-structured-concurrency.md.
|
|
*
|
|
* <p>Compile and run with --enable-preview.
|
|
*/
|
|
public class StructuredDemo {
|
|
|
|
/** The application's own failure type: what R_X lets us surface. */
|
|
static class OrderFailed extends Exception {
|
|
OrderFailed(Throwable cause) {
|
|
super("order failed: " + cause.getMessage(), cause);
|
|
}
|
|
}
|
|
|
|
static String fetchPrice() throws InterruptedException {
|
|
Thread.sleep(50);
|
|
return "price=42";
|
|
}
|
|
|
|
static String fetchStock() throws InterruptedException {
|
|
Thread.sleep(80);
|
|
return "stock=7";
|
|
}
|
|
|
|
static String failingCall() {
|
|
throw new IllegalStateException("inventory service is down");
|
|
}
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
allSuccessful();
|
|
yourOwnExceptionType();
|
|
timeout();
|
|
}
|
|
|
|
/** The default joiner: all subtasks must succeed, failure surfaces as ExecutionException. */
|
|
static void allSuccessful() throws Exception {
|
|
System.out.println("--- allSuccessfulOrThrow(): results as a List, failures as ExecutionException");
|
|
try (var scope = StructuredTaskScope.open(Joiner.<String>allSuccessfulOrThrow())) {
|
|
scope.fork(StructuredDemo::fetchPrice);
|
|
scope.fork(StructuredDemo::fetchStock);
|
|
List<String> results = scope.join();
|
|
System.out.println("results = " + results);
|
|
Check.that(results.equals(List.of("price=42", "stock=7")), "join() returns the subtask results in fork order");
|
|
}
|
|
|
|
try (var scope = StructuredTaskScope.open(Joiner.<String>allSuccessfulOrThrow())) {
|
|
scope.fork(StructuredDemo::fetchPrice);
|
|
scope.fork(StructuredDemo::failingCall);
|
|
scope.join();
|
|
Check.that(false, "unreachable");
|
|
} catch (ExecutionException e) {
|
|
System.out.println("caught " + e.getClass().getName() + " with cause " + e.getCause());
|
|
Check.that(e.getCause() instanceof IllegalStateException, "a failing subtask surfaces as ExecutionException(cause)");
|
|
}
|
|
}
|
|
|
|
/** New in 27: hand the joiner a function and join() throws YOUR type. */
|
|
static void yourOwnExceptionType() throws InterruptedException {
|
|
System.out.println("--- allSuccessfulOrThrow(Function): join() throws the exception type you choose");
|
|
try (var scope = StructuredTaskScope.open(Joiner.<String, OrderFailed>allSuccessfulOrThrow(OrderFailed::new))) {
|
|
scope.fork(StructuredDemo::fetchPrice);
|
|
scope.fork(StructuredDemo::failingCall);
|
|
scope.join(); // declared: throws OrderFailed, InterruptedException
|
|
Check.that(false, "unreachable");
|
|
} catch (OrderFailed e) {
|
|
System.out.println("caught " + e.getClass().getSimpleName() + ": " + e.getMessage());
|
|
Check.that(e.getCause() instanceof IllegalStateException, "the mapped exception wraps the original failure");
|
|
}
|
|
}
|
|
|
|
/** Timeouts are now a configuration option; a Joiner decides what a timeout means. */
|
|
static void timeout() throws Exception {
|
|
System.out.println("--- withTimeout(...): what happens when the scope runs out of time");
|
|
try (var scope = StructuredTaskScope.open(Joiner.<String>allSuccessfulOrThrow(),
|
|
cfg -> cfg.withTimeout(Duration.ofMillis(100)))) {
|
|
Subtask<String> slow = scope.fork(() -> {
|
|
Thread.sleep(2_000);
|
|
return "too late";
|
|
});
|
|
scope.join();
|
|
Check.that(false, "unreachable");
|
|
} catch (Exception e) {
|
|
System.out.println("caught " + e.getClass().getName());
|
|
System.out.println(" cause: " + e.getCause());
|
|
Check.that(e.getCause() instanceof StructuredTaskScope.CancelledByTimeoutException
|
|
|| e instanceof StructuredTaskScope.CancelledByTimeoutException,
|
|
"a timeout is reported as CancelledByTimeoutException");
|
|
}
|
|
}
|
|
}
|