Files

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");
}
}
}