Add structured-concurrency-final module: StructuredTaskScope final in JDK 28 (JEP 543), run on 28-ea, 27, 26 and 25

Runnable demos for the ankurm.com structured-concurrency-java article: the ExecutorService thread leak and its fix, every built-in Joiner,
structure violations, timeouts, a custom Joiner, JSON thread dumps (in-process and jcmd), scoped values, javap diffs of the API on 25, 26, 27 and 28-ea,
compiler output for six pieces of 25/26-era code, and the preview class-file trap between 27 and 28. Transcripts in output/.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01CifR2BE7PBvRy5hT8TS9oz
This commit is contained in:
Claude
2026-10-26 02:01:31 +00:00
parent 2e25042a68
commit c93a1fffbb
37 changed files with 1351 additions and 0 deletions
@@ -0,0 +1,53 @@
import com.sun.management.HotSpotDiagnosticMXBean;
import java.lang.management.ManagementFactory;
import java.nio.file.*;
import java.util.concurrent.*;
import java.util.concurrent.StructuredTaskScope.Joiner;
/** The thread dump shows the scope as a container, its owner, and the threads forked in it. */
public class ThreadDump {
public static void main(String[] args) throws Exception {
if (args.length > 0 && args[0].equals("hold")) { // used by run.sh: hold nested scopes open so jcmd can dump them
try (var scope = StructuredTaskScope.open(Joiner.<String>awaitAllSuccessfulOrThrow(), cf -> cf.withName("orders"))) {
scope.fork(() -> { Thread.sleep(6000); return "A"; });
scope.fork(() -> { // subtask B opens a child scope of its own
try (var child = StructuredTaskScope.open(Joiner.<String>awaitAllSuccessfulOrThrow(), cf -> cf.withName("inventory"))) {
child.fork(() -> { Thread.sleep(6000); return "B1"; });
child.join();
}
return "B";
});
scope.join();
}
return;
}
Path file = Files.createTempFile("dump", ".json"); Files.delete(file);
try (var scope = StructuredTaskScope.open(Joiner.<String>awaitAllSuccessfulOrThrow(), cf -> cf.withName("orders"))) {
for (int i = 0; i < 2; i++) {
int n = i;
scope.fork(() -> { Thread.sleep(1500); return "task-" + n; });
}
Thread.sleep(300); // let the subtasks start and block
ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class)
.dumpThreads(file.toString(), HotSpotDiagnosticMXBean.ThreadDumpFormat.JSON);
scope.join();
}
String json = Files.readString(file);
int at = json.indexOf("\"container\": \"orders");
int start = json.lastIndexOf('{', at);
int end = json.indexOf("\"threadCount\"", at);
String block = json.substring(start, end);
// identity hashes, thread ids and timestamps change between runs: mask them so the transcript is stable
block = block.replaceAll("@[0-9a-f]+", "@<hash>").replaceAll("\"tid\": \\d+", "\"tid\": <n>").replaceAll("\"owner\": \\d+", "\"owner\": <n>");
// keep the lines that show the hierarchy: the scope, its parent and owner, and each forked thread with its own frame
for (String line : block.split("\n")) {
String l = line.trim();
if (l.startsWith("\"container\"") || l.startsWith("\"parent\"") || l.startsWith("\"owner\"")
|| l.startsWith("\"tid\"") || l.startsWith("\"virtual\"") || l.startsWith("\"state\"")
|| l.contains("ThreadDump.lambda")) {
System.out.println(l);
}
}
Files.deleteIfExists(file);
}
}