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:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user