{"id":34113,"date":"2023-06-14T10:40:00","date_gmt":"2023-06-14T08:40:00","guid":{"rendered":"https:\/\/www.happycoders.eu\/?p=34113"},"modified":"2026-06-09T16:59:22","modified_gmt":"2026-06-09T14:59:22","slug":"structured-concurrency-structuredtaskscope","status":"publish","type":"post","link":"https:\/\/www.happycoders.eu\/de\/java\/structured-concurrency-structuredtaskscope\/","title":{"rendered":"Structured Concurrency in Java mit StructuredTaskScope"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><em>Structured Concurrency<\/em> wurde \u2013 zusammen mit <a href=\"\/de\/java\/virtual-threads\/\">virtuellen Threads<\/a> und <a href=\"\/de\/java\/scoped-values\/\">Scoped Values<\/a> \u2013 in <a href=\"https:\/\/openjdk.org\/projects\/loom\/\" target=\"_blank\" rel=\"noopener\">Project Loom<\/a> entwickelt. Sie durchlief zwei Incubator-Runden (Java 19 und Java 20) und seit Java 21 mehrere Preview-Runden. In der aktuellen Version <a href=\"https:\/\/www.happycoders.eu\/de\/java\/java-26-features\/#structured-concurrency-sixth-preview-jep-525\">Java 26<\/a> liegt sie als sechster Preview vor (<a href=\"https:\/\/openjdk.org\/jeps\/525\" target=\"_blank\" rel=\"noreferrer noopener\">JDK Enhancement Proposal 525<\/a>).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In <a href=\"https:\/\/www.happycoders.eu\/de\/java\/java-25-features\/#structured-concurrency-fifth-preview-jep-505\">Java 25<\/a> wurde die <code>StructuredTaskScope<\/code>-API durch <a href=\"https:\/\/openjdk.org\/jeps\/505\" target=\"_blank\" rel=\"noopener\">JEP 505<\/a> grundlegend \u00fcberarbeitet: <code>StructuredTaskScope<\/code> und die Join-Strategie wurden entkoppelt \u2013 Stichwort \u201e<a href=\"https:\/\/de.wikipedia.org\/wiki\/Komposition_an_Stelle_von_Vererbung\" target=\"_blank\" rel=\"noreferrer noopener\">Composition over inheritance<\/a>\". Die Beispiele in diesem Artikel verwenden durchgehend diese neue API. Wo sich von Java 25 auf Java 26 noch etwas ge\u00e4ndert hat, weise ich jeweils in einer Infobox darauf hin.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In diesem Artikel erf\u00e4hrst du:<\/p>\n\n\n\n<ul class=\"wp-block-list hc-checked-list\">\n<li>Warum ben\u00f6tigen wir Structured Concurrency?<\/li>\n\n\n\n<li>Was ist Structured Concurrency?<\/li>\n\n\n\n<li>Wie wird <code>StructuredTaskScope<\/code> verwendet?<\/li>\n\n\n\n<li>Was ist eine <em>Policy<\/em>? Welche Policies gibt es, und wie k\u00f6nnen wir selbst eine schreiben?<\/li>\n\n\n\n<li>Was ist der Vorteil von Structured Concurrency?<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Eine begleitende Demo-Anwendung findest du in diesem <a href=\"https:\/\/github.com\/SvenWoltmann\/structured-concurrency\" target=\"_blank\" rel=\"noopener\">GitHub-Repository<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Schauen wir uns zuerst einmal an, wie wir nebenl\u00e4ufige Teilaufgaben bisher implementiert haben.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"warum-benoetigen-wir-structured-concurrency\">Warum ben\u00f6tigen wir Structured Concurrency?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn eine Aufgabe aus verschiedenen \u2013 vor allem blockierenden \u2013 Teilaufgaben besteht, die nebenl\u00e4ufig erledigt werden k\u00f6nnen (z. B. Zugriff auf Daten aus einer Datenbank oder Aufruf einer Remote-API), so konnten wir hierf\u00fcr bisher das Java-Executor-Framework einsetzen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das k\u00f6nnte dann z. B. so aussehen (Klasse <a href=\"https:\/\/github.com\/SvenWoltmann\/structured-concurrency\/blob\/main\/src\/main\/java\/eu\/happycoders\/structuredconcurrency\/demo1_invoice\/InvoiceGenerator3_ThreadPool.java\" target=\"_blank\" rel=\"noopener\">InvoiceGenerator3_ThreadPool<\/a> in der Demo-Anwendung):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-function\">Invoice <span class=\"hljs-title\">createInvoice<\/span><span class=\"hljs-params\">(<span class=\"hljs-keyword\">int<\/span> orderId, <span class=\"hljs-keyword\">int<\/span> customerId, String language)<\/span>\n        <span class=\"hljs-keyword\">throws<\/span> InterruptedException, ExecutionException <\/span>{\n    Future&lt;Order&gt; orderFuture = \n            executor.submit(() -&gt; orderService.getOrder(orderId));\n\n    Future&lt;Customer&gt; customerFuture =\n            executor.submit(() -&gt; customerService.getCustomer(customerId));\n\n    Future&lt;InvoiceTemplate&gt; invoiceTemplateFuture =\n            executor.submit(() -&gt; invoiceTemplateService.getTemplate(language));\n\n    Order order = orderFuture.get();\n    Customer customer = customerFuture.get();\n    InvoiceTemplate invoiceTemplate = invoiceTemplateFuture.get();\n\n    <span class=\"hljs-keyword\">return<\/span> Invoice.generate(order, customer, invoiceTemplate);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Wir \u00fcbergeben die drei Teilaufgaben an den Executor und warten auf die Teilergebnisse. Der Happy Path ist schnell implementiert. Aber wie behandeln wir Ausnahmen?<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Wenn in einem Subtask ein Fehler auftritt \u2013 wie k\u00f6nnen wir dann die anderen Subtasks abbrechen? Wenn im Beispiel oben <code>orderService.getOrder(\u2026)<\/code> fehlschl\u00e4gt, dann wirft <code>orderFuture.get()<\/code> eine Exception, die <code>createInvoice(\u2026)<\/code>-Methode endet, und wir haben evtl. zwei noch weiterlaufende Threads.<\/li>\n\n\n\n<li>Wie k\u00f6nnen wir die Subtasks abbrechen, wenn der Parent Task (\u201eErstelle eine Rechnung\") abgebrochen wird \u2013 oder wenn die komplette Anwendung heruntergefahren wird?<\/li>\n\n\n\n<li>Wie k\u00f6nnen wir \u2013 in einem alternativen Use Case \u2013 verbleibende Subtasks abbrechen, wenn lediglich das Ergebnis eines einzigen Subtasks ben\u00f6tigt wird?<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Alles ist machbar, erfordert aber \u00e4u\u00dferst komplexen, schwer wartbaren Code (im GitHub-Repository findest du zwei Beispiele daf\u00fcr: <a href=\"https:\/\/github.com\/SvenWoltmann\/structured-concurrency\/blob\/main\/src\/main\/java\/eu\/happycoders\/structuredconcurrency\/demo1_invoice\/InvoiceGenerator2b_CompletableFutureCancelling.java\" target=\"_blank\" rel=\"noopener\">InvoiceGenerator2b_CompletableFutureCancelling<\/a> und <a href=\"https:\/\/github.com\/SvenWoltmann\/structured-concurrency\/blob\/main\/src\/main\/java\/eu\/happycoders\/structuredconcurrency\/demo1_invoice\/InvoiceGenerator4b_NewVirtualThreadPerTaskCancelling.java\" target=\"_blank\" rel=\"noopener\">InvoiceGenerator4b_NewVirtualThreadPerTaskCancelling<\/a>).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Und was, wenn wir Code dieser Art debuggen m\u00f6chten? Ein Thread-Dump z. B. w\u00fcrde uns haufenweise Threads mit dem Namen \u201epool-X-thread-Y\" liefern \u2013 wir w\u00fcssten aber nicht, welcher Pool-Thread zu welchem aufrufenden Thread geh\u00f6rt, da sich alle aufrufenden Threads den Thread-Pool des Executors teilen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"was-ist-unstructured-concurrency\">Was ist Unstructured Concurrency?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u201eUnstructured Concurrency\" bedeutet, dass unsere Tasks in einem Netz von Threads ablaufen, deren Start und Ende im Code schwer erkennbar ist. Eine saubere Fehlerbehandlung ist meist nicht vorhanden, und oft kommt es zu verwaisten Threads, wenn eine Kontrollstruktur (im Beispiel oben: die <code>createInvoice(\u2026)<\/code>-Methode) endet:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-half_400\"><img decoding=\"async\" width=\"400\" height=\"341\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/unstructured-concurrency.v2-400x341.png\" alt=\"Unstructured Concurrency\" class=\"wp-image-37257\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/unstructured-concurrency.v2-400x341.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/unstructured-concurrency.v2-224x191.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/unstructured-concurrency.v2-336x286.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/unstructured-concurrency.v2-504x430.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/unstructured-concurrency.v2-672x573.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/unstructured-concurrency.v2-600x512.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/unstructured-concurrency.v2.png 800w\" sizes=\"(max-width: 400px) 100vw, 400px\" \/><figcaption class=\"wp-element-caption\">Unstructured Concurrency<\/figcaption><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"was-ist-structured-concurrency\">Was ist Structured Concurrency?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Structured Concurrency<\/em> ist ein Konzept, das die Implementierung, Lesbarkeit und Wartbarkeit von Code f\u00fcr die Aufteilung einer Aufgabe in Teilaufgaben und deren nebenl\u00e4ufige Abarbeitung erheblich verbessert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dazu f\u00fchrt sie mit der Klasse <code>StructuredTaskScope<\/code> eine Kontrollstruktur ein, die <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>einen klaren Scope definiert, an dessen Anfang die Threads der Teilaufgaben starten und an dessen Ende die Threads der Teilaufgaben enden,<\/li>\n\n\n\n<li>eine saubere Fehlerbehandlung erm\u00f6glicht,<\/li>\n\n\n\n<li>und einen sauberen Abbruch von Teilaufgaben erlaubt, deren Ergebnisse nicht mehr ben\u00f6tigt werden.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Was das genau bedeutet, zeige ich dir in den folgenden Abschnitten an mehreren Beispielen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"structuredtaskscope-ein-erstes-beispiel\">StructuredTaskScope \u2013 ein erstes Beispiel<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Structured Concurrency<\/em> wird mit der Klasse <code>StructuredTaskScope<\/code> implementiert. Mit dieser Klasse k\u00f6nnen wir das Beispiel von oben wie folgt umschreiben (Klasse <a href=\"https:\/\/github.com\/SvenWoltmann\/structured-concurrency\/blob\/main\/src\/main\/java\/eu\/happycoders\/structuredconcurrency\/demo1_invoice\/InvoiceGenerator5_StructuredTaskScope.java\" target=\"_blank\" rel=\"noopener\">InvoiceGenerator5_StructuredTaskScope<\/a> in der Demo-Anwendung):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-function\">Invoice <span class=\"hljs-title\">createInvoice<\/span><span class=\"hljs-params\">(<span class=\"hljs-keyword\">int<\/span> orderId, <span class=\"hljs-keyword\">int<\/span> customerId, String language)<\/span>\n        <span class=\"hljs-keyword\">throws<\/span> InterruptedException <\/span>{\n    <span class=\"hljs-keyword\">try<\/span> (<span class=\"hljs-keyword\">var<\/span> scope = StructuredTaskScope.open()) {\n        Subtask&lt;Order&gt; orderSubtask =\n                scope.fork(() -&gt; orderService.getOrder(orderId));\n\n        Subtask&lt;Customer&gt; customerSubtask =\n                scope.fork(() -&gt; customerService.getCustomer(customerId));\n\n        Subtask&lt;InvoiceTemplate&gt; invoiceTemplateSubtask =\n                scope.fork(() -&gt; invoiceTemplateService.getTemplate(language));\n\n        scope.join();\n\n        Order order = orderSubtask.get();\n        Customer customer = customerSubtask.get();\n        InvoiceTemplate template = invoiceTemplateSubtask.get();\n\n        <span class=\"hljs-keyword\">return<\/span> Invoice.generate(order, customer, template);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"erlaeuterung\">Erl\u00e4uterung<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Verglichen mit dem Unstructured-Concurrency-Beispiel ersetzen wir den im Scope der <em>Klasse<\/em> liegenden <code>ExecutorService<\/code> durch einen im Scope der <em>Methode<\/em> liegenden <code>StructuredTaskScope<\/code> \u2013 und <code>executor.submit()<\/code> durch <code>scope.fork()<\/code>. Den Scope \u00f6ffnen wir \u00fcber die statische Methode <code>StructuredTaskScope.open()<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mit <code>scope.fork(\u2026)<\/code> starten wir die Subtasks; jeder l\u00e4uft standardm\u00e4\u00dfig in einem eigenen virtuellen Thread. Mit <code>scope.join()<\/code> warten wir darauf, dass alle Tasks erledigt sind. Das Risiko verwaister Tasks besteht damit nicht mehr.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Danach k\u00f6nnen wir \u00fcber <code>Subtask.get()<\/code> die Ergebnisse der drei Tasks auslesen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"fehlerbehandlung-mit-structuredtaskscope\">Fehlerbehandlung mit StructuredTaskScope<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Was passiert, wenn einer der drei Subtasks fehlschl\u00e4gt?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein mit <code>StructuredTaskScope.open()<\/code> (ohne Argument) ge\u00f6ffneter Scope verwendet die Standard-Policy: Sobald ein Subtask eine Exception wirft, werden alle anderen Subtasks abgebrochen, und <code>scope.join()<\/code> wirft die aufgetretene Exception \u2013 verpackt in eine <code>StructuredTaskScope.FailedException<\/code> mit der urspr\u00fcnglichen Exception als \u201eCause\".<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">M\u00f6chtest du die Exception nach dem Schlie\u00dfen des Scopes behandeln, erg\u00e4nzt du einen <code>catch<\/code>-Block:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">try<\/span> (<span class=\"hljs-keyword\">var<\/span> scope = StructuredTaskScope.open()) {\n    <span class=\"hljs-comment\">\/\/ fork(\u2026) und join() ...<\/span>\n} <span class=\"hljs-keyword\">catch<\/span> (StructuredTaskScope.FailedException e) {\n    Throwable cause = e.getCause();\n    <span class=\"hljs-keyword\">switch<\/span> (cause) {\n        <span class=\"hljs-keyword\">case<\/span> OrderNotFoundException onfe -&gt; <span class=\"hljs-comment\">\/\/ ...<\/span>\n        <span class=\"hljs-keyword\">default<\/span> -&gt; <span class=\"hljs-comment\">\/\/ ...<\/span>\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div class=\"wp-block-uagb-container uagb-block-8ae4ca2e alignfull uagb-is-root-container\">\n<div class=\"uagb-block-b769b372 uagb-infobox__content-wrap  uagb-infobox-icon-left uagb-infobox-left uagb-infobox-stacked-mobile uagb-infobox-image-valign-top \"><div class=\"uagb-ifb-icon-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z\"><\/path><\/svg><\/div><div class=\"uagb-ifb-content\"><div class=\"uagb-ifb-title-wrap\"><\/div><p class=\"uagb-ifb-desc\"><strong>Ausblick: Java 27<br><\/strong><br>In <a href=\"https:\/\/www.happycoders.eu\/de\/java\/java-27-features\/#structured-concurrency-seventh-preview-jep-533\">Java 27<\/a> (<a href=\"https:\/\/openjdk.org\/jeps\/533\">JEP 533, siebter Preview<\/a>) \u00e4ndert sich der geworfene Exception-Typ: Die Standard-Joiner werfen dann nicht mehr eine <code>FailedException<\/code>, sondern die aus <code>Future.get()<\/code> bekannte <code>ExecutionException<\/code>. Die urspr\u00fcngliche Exception bleibt \u00fcber <code>getCause()<\/code> erreichbar.<\/p><\/div><\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"den-beispielcode-ausfuehren\">Den Beispielcode ausf\u00fchren<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Falls du das Beispiel selbst ausprobieren m\u00f6chtest: <code>StructuredTaskScope<\/code> ist in Java 26 noch ein Preview-Feature und muss explizit freigeschaltet werden. Beim Kompilieren mit <code>javac --release 26 --enable-preview<\/code>, beim Ausf\u00fchren mit <code>java --enable-preview<\/code>. Eine genaue Anleitung findest du in der <a href=\"https:\/\/github.com\/SvenWoltmann\/structured-concurrency\/blob\/main\/README.md\" target=\"_blank\" rel=\"noopener\">README<\/a> der Demo-Anwendung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Im Beispielcode werfen die drei Subtasks mit einer gewissen Wahrscheinlichkeit eine Exception. Wenn du das Programm ein paar Mal startest, wirst du sehen, wie eine Exception in einem Task zu einer Interruption in den anderen Tasks und einer Beendigung des Programms f\u00fchrt:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">$ java -cp target\/classes --enable-preview \\\n    eu.happycoders.structuredconcurrency.demo1_invoice.InvoiceGenerator5_StructuredTaskScope\n\n&#091;Thread&#091;#1,main,5,main]] Forking tasks\n&#091;Thread&#091;#1,main,5,main]] Waiting for all tasks to finish or one to fail\n&#091;VirtualThread&#091;#31]\/runnable@ForkJoinPool-1-worker-2] Loading customer\n&#091;VirtualThread&#091;#29]\/runnable@ForkJoinPool-1-worker-3] Loading order\n&#091;VirtualThread&#091;#35]\/runnable@ForkJoinPool-1-worker-1] Loading template\n&#091;VirtualThread&#091;#31]\/runnable@ForkJoinPool-1-worker-1] Finished loading customer\n&#091;VirtualThread&#091;#29]\/runnable@ForkJoinPool-1-worker-2] Error loading order\n&#091;VirtualThread&#091;#35]\/runnable@ForkJoinPool-1-worker-1] Template loading was interrupted\nException in thread \"main\" java.util.concurrent.StructuredTaskScope$FailedException: java.lang.RuntimeException: Error loading order\n        &#091;...]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">An dieser Ausgabe siehst du \u00fcbrigens auch, dass alle Tasks in <a href=\"\/de\/java\/virtual-threads\/\">virtuellen Threads<\/a> ausgef\u00fchrt werden.<\/p>\n\n\n<div class=\"convertkit-form wp-block-convertkit-form\" style=\"\"><script async data-uid=\"1427197203\" src=\"https:\/\/happycoders.kit.com\/1427197203\/index.js\" data-jetpack-boost=\"ignore\" data-no-defer=\"1\" data-no-optimize=\"1\" nowprocket><\/script><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"policies-ueber-joiner\">Policies \u00fcber Joiner<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Eine sogenannte <em>Policy<\/em> definiert, was passiert, wenn ein Subtask beendet wird oder eine Exception wirft. Au\u00dferdem kann eine Policy einen R\u00fcckgabewert f\u00fcr <code>scope.join()<\/code> definieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Im Beispiel oben haben wir die Standard-Policy verwendet (\u201eShutdown on Failure\"). Andere Policies w\u00e4hlst du, indem du der <code>open<\/code>-Methode einen <code>Joiner<\/code> \u00fcbergibst. Ein <code>Joiner<\/code> behandelt die Fertigstellung der Subtasks und erzeugt das Ergebnis f\u00fcr <code>scope.join()<\/code>. Je nach Joiner liefert <code>join()<\/code> ein einzelnes Ergebnis, eine Liste oder <code>null<\/code> zur\u00fcck.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"any-successful-result\">\u201eAny Successful Result\"<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Manchmal brauchen wir nicht <em>alle<\/em> Ergebnisse, sondern nur das erste erfolgreiche. Beispiel: Wir wollen eine Kundenadresse \u00fcber mehrere externe APIs gleichzeitig verifizieren und nur das erste Ergebnis verwenden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Daf\u00fcr gibt es den Joiner <code>anySuccessfulOrThrow()<\/code>. Sobald ein Subtask erfolgreich war, wird der Scope beendet und die \u00fcbrigen Subtasks werden abgebrochen. <code>scope.join()<\/code> liefert dann das Ergebnis des erfolgreichen Subtasks (Klasse <a href=\"https:\/\/github.com\/SvenWoltmann\/structured-concurrency\/blob\/main\/src\/main\/java\/eu\/happycoders\/structuredconcurrency\/demo2_address\/AddressVerification2_AnySuccessful.java\" target=\"_blank\" rel=\"noopener\">AddressVerification2_AnySuccessful<\/a> in der Demo-Anwendung):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-function\">AddressVerificationResponse <span class=\"hljs-title\">verifyAddress<\/span><span class=\"hljs-params\">(Address address)<\/span> <span class=\"hljs-keyword\">throws<\/span> InterruptedException <\/span>{\n    <span class=\"hljs-keyword\">try<\/span> (<span class=\"hljs-keyword\">var<\/span> scope = StructuredTaskScope.open(\n            Joiner.&lt;AddressVerificationResponse&gt;anySuccessfulOrThrow())) {\n        log(<span class=\"hljs-string\">\"Forking tasks\"<\/span>);\n\n        scope.fork(() -&gt; verificationService.verifyViaServiceA(address));\n        scope.fork(() -&gt; verificationService.verifyViaServiceB(address));\n        scope.fork(() -&gt; verificationService.verifyViaServiceC(address));\n\n        log(<span class=\"hljs-string\">\"Waiting for one task to finish\"<\/span>);\n\n        <span class=\"hljs-keyword\">return<\/span> scope.join();\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Sollten wider Erwarten alle drei Aufrufe eine Exception werfen, wirft <code>scope.join()<\/code> die erste davon, eingebettet in eine <code>FailedException<\/code>.<\/p>\n\n\n\n<div class=\"wp-block-uagb-container uagb-block-30b76080 alignfull uagb-is-root-container\">\n<div class=\"uagb-block-264626de uagb-infobox__content-wrap  uagb-infobox-icon-left uagb-infobox-left uagb-infobox-stacked-mobile uagb-infobox-image-valign-top \"><div class=\"uagb-ifb-icon-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z\"><\/path><\/svg><\/div><div class=\"uagb-ifb-content\"><div class=\"uagb-ifb-title-wrap\"><\/div><p class=\"uagb-ifb-desc\"><strong>Java 25 \u2192 Java 26<\/strong><br><br>In Java 25 hie\u00df die Methode <code>Joiner.anySuccessfulOrThrow()<\/code> noch <code>anySuccessfulResultOrThrow()<\/code>. Das Wort \u201eResult\u201c wurde entfernt, da es in den anderen Joiner-Namen ebenfalls nicht vorkommt.<\/p><\/div><\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn du den Beispielcode ausf\u00fchrst, siehst du, wie der erste erfolgreiche Subtask zu einem Ergebnis f\u00fchrt und die anderen Tasks abgebrochen werden:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">$ java -cp target\/classes --enable-preview \\\n    eu.happycoders.structuredconcurrency.demo2_address.AddressVerification2_AnySuccessful\n\n&#091;Thread&#091;#1,main,5,main]] Forking tasks\n&#091;Thread&#091;#1,main,5,main]] Waiting for one task to finish\n&#091;VirtualThread&#091;#31]\/runnable@ForkJoinPool-1-worker-2] Verifying address via service B\n&#091;VirtualThread&#091;#29]\/runnable@ForkJoinPool-1-worker-3] Verifying address via service A\n&#091;VirtualThread&#091;#34]\/runnable@ForkJoinPool-1-worker-1] Verifying address via service C\n&#091;VirtualThread&#091;#34]\/runnable@ForkJoinPool-1-worker-1] Finished loading address via service C\n&#091;Thread&#091;#1,main,5,main]] Retrieving result\n&#091;VirtualThread&#091;#31]\/runnable@ForkJoinPool-1-worker-3] Verifying address via service B was interrupted\n&#091;VirtualThread&#091;#29]\/runnable@ForkJoinPool-1-worker-2] Verifying address via service A was interrupted<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"alle-joiner-im-ueberblick\">Alle Joiner im \u00dcberblick<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In Java 26 stehen dir die folgenden vordefinierten Joiner zur Verf\u00fcgung:<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table><thead><tr><th>Joiner<\/th><th>Beschreibung<\/th><\/tr><\/thead><tbody><tr><td>ohne Joiner oder<br><code>Joiner.awaitAllSuccessfulOrThrow()<\/code><\/td><td>Eine Exception in einem Subtask f\u00fchrt umgehend zum Abbruch des Scopes; <code>scope.join()<\/code> wirft die Exception gewrappt in eine <code>FailedException<\/code>. Sind alle Subtasks erfolgreich, endet <code>scope.join()<\/code> ohne Exception und gibt <code>null<\/code> zur\u00fcck. Die Ergebnisse m\u00fcssen aus den von <code>scope.fork()<\/code> zur\u00fcckgegebenen <code>Subtask<\/code>-Objekten ausgelesen werden.<\/td><\/tr><tr><td><code>Joiner.awaitAll()<\/code><\/td><td><code>scope.join()<\/code> wartet darauf, dass alle Subtasks beendet sind \u2013 egal ob erfolgreich oder nicht. Gibt in jedem Fall <code>null<\/code> zur\u00fcck; die Ergebnisse m\u00fcssen aus den <code>Subtask<\/code>-Objekten ausgelesen werden.<\/td><\/tr><tr><td><code>Joiner<\/code><code>.anySuccessfulOrThrow()<\/code><\/td><td><code>scope.join()<\/code> liefert das Ergebnis des ersten erfolgreichen Subtasks; die anderen Subtasks werden abgebrochen. Schlagen alle fehl, wirft <code>scope.join()<\/code> die Exception des ersten fehlgeschlagenen Subtasks gewrappt in eine <code>FailedException<\/code>.<\/td><\/tr><tr><td><code>Joiner.allSuccessfulOrThrow()<\/code><\/td><td>Wie ohne Joiner bzw. mit <code>awaitAllSuccessfulOrThrow()<\/code> \u2013 mit dem Unterschied, dass <code>scope.join()<\/code> bei Erfolg eine <code>List<\/code> der Ergebnisse zur\u00fcckliefert. Eine Exception f\u00fchrt zum Abbruch des Scopes und einer <code>FailedException<\/code>.<\/td><\/tr><tr><td><code>Joiner.allUntil(<br>&nbsp; &nbsp; Predicate&lt;Subtask&lt;T&gt;&gt; isDone)<\/code><\/td><td><code>scope.join()<\/code> wartet, bis entweder alle Subtasks beendet sind \u2013 egal ob erfolgreich oder nicht \u2013 oder das \u00fcbergebene Predicate auf einen beendeten Subtask matcht. Liefert eine <code>List<\/code> der <code>Subtask<\/code>-Objekte zur\u00fcck.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<div class=\"wp-block-uagb-container uagb-block-34e83e33 alignfull uagb-is-root-container\">\n<div class=\"uagb-block-7494e5d5 uagb-infobox__content-wrap  uagb-infobox-icon-left uagb-infobox-left uagb-infobox-stacked-mobile uagb-infobox-image-valign-top \"><div class=\"uagb-ifb-icon-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z\"><\/path><\/svg><\/div><div class=\"uagb-ifb-content\"><div class=\"uagb-ifb-title-wrap\"><\/div><p class=\"uagb-ifb-desc\"><strong>Java 25 \u2192 Java 26<\/strong><br><br>In Java 25 lieferte <code>allSuccessfulOrThrow()<\/code> noch einen <code>Stream<\/code> der <code>Subtask<\/code>-Objekte. Seit Java 26 gibt <code>scope.join()<\/code> direkt eine <code>List<\/code> der Ergebnisse zur\u00fcck.<\/p><\/div><\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"eigene-policy-ein-benutzerdefinierter-joiner\">Eigene Policy: ein benutzerdefinierter Joiner<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Sollte keiner der vordefinierten Joiner f\u00fcr deinen Einsatzzweck geeignet sein, kannst du mit relativ geringem Aufwand einen eigenen schreiben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nehmen wir an, wir wollen die Verf\u00fcgbarkeit eines Produkts bei mehreren Lieferanten pr\u00fcfen \u2013 und zwar nicht das erste Ergebnis verwenden, sondern das mit der schnellsten Verf\u00fcgbarkeit. Gleichzeitig wollen wir fehlgeschlagene Anfragen nur dann propagieren, wenn die Anfragen bei allen Lieferanten fehlgeschlagen sind.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das l\u00e4sst sich \u00fcberraschend einfach \u2013 und zugleich f\u00fcr andere Einsatzszenarien wiederverwendbar \u2013 realisieren, indem wir das <code>Joiner<\/code>-Interface implementieren (Klasse <a href=\"https:\/\/github.com\/SvenWoltmann\/structured-concurrency\/blob\/main\/src\/main\/java\/eu\/happycoders\/structuredconcurrency\/demo3_suppliers\/BestResultJoiner.java\" target=\"_blank\" rel=\"noopener\">BestResultJoiner<\/a> in der Demo-Anwendung):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">BestResultJoiner<\/span>&lt;<span class=\"hljs-title\">T<\/span>&gt; <span class=\"hljs-keyword\">implements<\/span> <span class=\"hljs-title\">Joiner<\/span>&lt;<span class=\"hljs-title\">T<\/span>, <span class=\"hljs-title\">T<\/span>&gt; <\/span>{\n\n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> Comparator&lt;T&gt; comparator;\n\n    <span class=\"hljs-keyword\">private<\/span> T bestResult;\n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> List&lt;Throwable&gt; exceptions =\n            Collections.synchronizedList(<span class=\"hljs-keyword\">new<\/span> ArrayList&lt;&gt;());\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-title\">BestResultJoiner<\/span><span class=\"hljs-params\">(Comparator&lt;T&gt; comparator)<\/span> <\/span>{\n        <span class=\"hljs-keyword\">this<\/span>.comparator = comparator;\n    }\n\n    <span class=\"hljs-meta\">@Override<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">boolean<\/span> <span class=\"hljs-title\">onComplete<\/span><span class=\"hljs-params\">(Subtask&lt;? extends T&gt; subtask)<\/span> <\/span>{\n        <span class=\"hljs-keyword\">switch<\/span> (subtask.state()) {\n            <span class=\"hljs-keyword\">case<\/span> UNAVAILABLE -&gt; {\n                <span class=\"hljs-comment\">\/\/ Ignore<\/span>\n            }\n            <span class=\"hljs-keyword\">case<\/span> SUCCESS -&gt; {\n                T result = subtask.get();\n                <span class=\"hljs-keyword\">synchronized<\/span> (<span class=\"hljs-keyword\">this<\/span>) {\n                    <span class=\"hljs-keyword\">if<\/span> (bestResult == <span class=\"hljs-keyword\">null<\/span> \n                            || comparator.compare(result, bestResult) &gt; <span class=\"hljs-number\">0<\/span>) {\n                        bestResult = result;\n                    }\n                }\n            }\n            <span class=\"hljs-keyword\">case<\/span> FAILED -&gt; exceptions.add(subtask.exception());\n        }\n\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">false<\/span>; <span class=\"hljs-comment\">\/\/ Don't cancel the scope<\/span>\n    }\n\n    <span class=\"hljs-meta\">@Override<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> T <span class=\"hljs-title\">result<\/span><span class=\"hljs-params\">()<\/span> <span class=\"hljs-keyword\">throws<\/span> SupplierDeliveryTimeCheckException <\/span>{\n        <span class=\"hljs-keyword\">if<\/span> (bestResult != <span class=\"hljs-keyword\">null<\/span>) {\n            <span class=\"hljs-keyword\">return<\/span> bestResult;\n        } <span class=\"hljs-keyword\">else<\/span> {\n            SupplierDeliveryTimeCheckException exception =\n                    <span class=\"hljs-keyword\">new<\/span> SupplierDeliveryTimeCheckException();\n            exceptions.forEach(exception::addSuppressed);\n            <span class=\"hljs-keyword\">throw<\/span> exception;\n        }\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Die <code>onComplete()<\/code>-Methode wird f\u00fcr jeden beendeten Subtask aufgerufen \u2013 sowohl f\u00fcr erfolgreiche als auch f\u00fcr solche, die eine Exception geworfen haben. Welcher Fall eingetreten ist, pr\u00fcfen wir mit <code>subtask.state()<\/code>. Im Erfolgsfall holen wir mit <code>subtask.get()<\/code> das Resultat und schreiben es \u2013 sofern es besser ist als das bisher beste \u2013 threadsicher in das Feld <code>bestResult<\/code>. Im Fall einer Exception sammeln wir diese threadsicher in einer Liste.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der R\u00fcckgabewert von <code>onComplete()<\/code> gibt an, ob der Scope abgebrochen werden soll (<code>true<\/code> steht f\u00fcr abbrechen). Da wir auf alle Lieferanten warten wollen, geben wir hier immer <code>false<\/code> zur\u00fcck.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die <code>result()<\/code>-Methode pr\u00fcft, ob ein erfolgreiches Ergebnis vorliegt, und gibt dieses zur\u00fcck. Andernfalls wirft sie eine <code>SupplierDeliveryTimeCheckException<\/code>, an die sie die gesammelten Exceptions als \u201esuppressed exceptions\u201c anh\u00e4ngt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Den Joiner setzen wir wie folgt ein (Klasse <a href=\"https:\/\/github.com\/SvenWoltmann\/structured-concurrency\/blob\/java-21\/src\/main\/java\/eu\/happycoders\/structuredconcurrency\/demo3_suppliers\/SupplierDeliveryTimeCheck2_StructuredTaskScope.java\" target=\"_blank\" rel=\"noreferrer noopener\">SupplierDeliveryTimeCheck2_StructuredTaskScope<\/a> in der Demo-Anwendung):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-function\">SupplierDeliveryTime <span class=\"hljs-title\">getSupplierDeliveryTime<\/span><span class=\"hljs-params\">(String productId, List&lt;String&gt; supplierIds)<\/span>\n        <span class=\"hljs-keyword\">throws<\/span> InterruptedException <\/span>{\n    <span class=\"hljs-keyword\">try<\/span> (<span class=\"hljs-keyword\">var<\/span> scope = StructuredTaskScope.open(\n            <span class=\"hljs-keyword\">new<\/span> BestResultJoiner&lt;SupplierDeliveryTime&gt;(\n                    Comparator.comparing(\n                            SupplierDeliveryTime::deliveryTimeHours).reversed()))) {\n        <span class=\"hljs-keyword\">for<\/span> (String supplierId : supplierIds) {\n            scope.fork(() -&gt; service.getDeliveryTime(productId, supplierId));\n        }\n\n        <span class=\"hljs-keyword\">return<\/span> scope.join();\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><code>scope.join()<\/code> liefert hier das Ergebnis der <code>result()<\/code>-Methode unseres Joiners zur\u00fcck. Schlagen alle Lieferanten-Anfragen fehl, wirft <code>result()<\/code> eine <code>SupplierDeliveryTimeCheckException<\/code> \u2013 und <code>scope.join()<\/code> verpackt diese in eine <code>FailedException<\/code> (mit der <code>SupplierDeliveryTimeCheckException<\/code> als \u201eCause\"). M\u00f6chtest du die urspr\u00fcngliche Exception behandeln, fragst du sie also \u00fcber <code>getCause()<\/code> ab.<\/p>\n\n\n\n<div class=\"wp-block-uagb-container uagb-block-11713268 alignfull uagb-is-root-container\">\n<div class=\"uagb-block-e6e5e822 uagb-infobox__content-wrap  uagb-infobox-icon-left uagb-infobox-left uagb-infobox-stacked-mobile uagb-infobox-image-valign-top \"><div class=\"uagb-ifb-icon-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z\"><\/path><\/svg><\/div><div class=\"uagb-ifb-content\"><div class=\"uagb-ifb-title-wrap\"><\/div><p class=\"uagb-ifb-desc\"><strong>Ausblick: Java 27<\/strong><br><br>In Java 27 bekommen <code>StructuredTaskScope<\/code> und <code>Joiner<\/code> einen dritten Typparameter <code>R_X<\/code> f\u00fcr den Exception-Typ, den <code>join()<\/code> werfen kann \u2013 aus <code>Joiner&lt;T, R&gt;<\/code> wird <code>Joiner&lt;T, R, R_X&gt;<\/code>. F\u00fcr Code, der die vordefinierten Joiner \u00fcber <code>open()<\/code> nutzt, \u00e4ndert sich nichts: Der Compiler leitet alles her. <br><br>Schreibst du aber einen eigenen Joiner wie diesen hier, wird die geworfene Exception Teil der Typsignatur. <code>join()<\/code> kann dann z. B. direkt eine <code>SupplierDeliveryTimeCheckException<\/code> werfen, statt sie in eine <code>ExecutionException<\/code> zu verpacken \u2013 Aufrufer bekommen so einen pr\u00e4zisen, gepr\u00fcften Exception-Vertrag auf <code>join()<\/code>.<\/p><\/div><\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Die Ausgabe des Beispielprogramms k\u00f6nnte z. B. so aussehen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">$ java -cp target\/classes --enable-preview \\\n    eu.happycoders.structuredconcurrency.demo3_suppliers.SupplierDeliveryTimeCheck2_StructuredTaskScope\n\n&#091;VirtualThread&#091;#31]\/runnable@ForkJoinPool-1-worker-2] Retrieving delivery time from supplier B\n&#091;VirtualThread&#091;#33]\/runnable@ForkJoinPool-1-worker-4] Retrieving delivery time from supplier D\n&#091;VirtualThread&#091;#34]\/runnable@ForkJoinPool-1-worker-3] Retrieving delivery time from supplier E\n&#091;VirtualThread&#091;#32]\/runnable@ForkJoinPool-1-worker-5] Retrieving delivery time from supplier C\n&#091;VirtualThread&#091;#29]\/runnable@ForkJoinPool-1-worker-3] Retrieving delivery time from supplier A\n&#091;VirtualThread&#091;#31]\/runnable@ForkJoinPool-1-worker-3] Error retrieving delivery time from supplier B\n&#091;VirtualThread&#091;#29]\/runnable@ForkJoinPool-1-worker-5] Finished retrieving delivery time from supplier A: 110 hours\n&#091;VirtualThread&#091;#32]\/runnable@ForkJoinPool-1-worker-3] Finished retrieving delivery time from supplier C: 104 hours\n&#091;VirtualThread&#091;#34]\/runnable@ForkJoinPool-1-worker-3] Error retrieving delivery time from supplier E\n&#091;VirtualThread&#091;#33]\/runnable@ForkJoinPool-1-worker-3] Finished retrieving delivery time from supplier D: 51 hours\n&#091;Thread&#091;#1,main,5,main]] Response: SupplierDeliveryTime&#091;supplier=D, deliveryTimeHours=51]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Sch\u00f6n zu sehen: Zwar waren die Aufrufe f\u00fcr die Lieferanten B und E fehlerhaft, die \u00fcbrigen Lieferanten haben aber Ergebnisse geliefert \u2013 und schlie\u00dflich wird das beste Ergebnis zur\u00fcckgeliefert: Lieferant D mit 51 Stunden Lieferzeit.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"timeout-fuer-einen-scope\">Timeout f\u00fcr einen Scope<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Was, wenn wir nicht beliebig lange auf die Subtasks warten wollen? \u00dcber die konfigurierende Variante der <code>open<\/code>-Methode kannst du dem Scope ein Timeout mitgeben:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-function\">List&lt;SupplierDeliveryTime&gt; <span class=\"hljs-title\">getSupplierDeliveryTimes<\/span><span class=\"hljs-params\">(\n        List&lt;String&gt; productIds, List&lt;String&gt; supplierIds, Duration timeout)<\/span>\n        <span class=\"hljs-keyword\">throws<\/span> InterruptedException <\/span>{\n    <span class=\"hljs-keyword\">try<\/span> (<span class=\"hljs-keyword\">var<\/span> scope = StructuredTaskScope.open(\n            Joiner.&lt;SupplierDeliveryTime&gt;allSuccessfulOrThrow(),\n            cf -&gt; cf.withTimeout(timeout))) {\n        productIds.forEach(productId -&gt; \n                scope.fork(() -&gt; getSupplierDeliveryTime(productId, supplierIds)));\n\n        <span class=\"hljs-keyword\">return<\/span> scope.join();\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">L\u00e4uft das Timeout ab, bevor alle Subtasks fertig sind, wird der Scope abgebrochen, alle noch laufenden Subtasks werden beendet, und <code>scope.join()<\/code> wirft eine <code>TimeoutException<\/code>.<\/p>\n\n\n\n<div class=\"wp-block-uagb-container uagb-block-d1da557b alignfull uagb-is-root-container\">\n<div class=\"uagb-block-e7831338 uagb-infobox__content-wrap  uagb-infobox-icon-left uagb-infobox-left uagb-infobox-stacked-mobile uagb-infobox-image-valign-top \"><div class=\"uagb-ifb-icon-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z\"><\/path><\/svg><\/div><div class=\"uagb-ifb-content\"><div class=\"uagb-ifb-title-wrap\"><\/div><p class=\"uagb-ifb-desc\"><strong>Java 25 \u2192 Java 26<\/strong><br><br>Die konfigurierende <code>open<\/code>-Methode nimmt seit Java 26 einen <code>UnaryOperator<\/code> f\u00fcr die Konfiguration entgegen (in Java 25 war es eine <code>Function<\/code>).<\/p><\/div><\/div>\n<\/div>\n\n\n\n<div class=\"wp-block-uagb-container uagb-block-20b4c4ba alignfull uagb-is-root-container\">\n<div class=\"uagb-block-d8adf683 uagb-infobox__content-wrap  uagb-infobox-icon-left uagb-infobox-left uagb-infobox-stacked-mobile uagb-infobox-image-valign-top \"><div class=\"uagb-ifb-icon-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z\"><\/path><\/svg><\/div><div class=\"uagb-ifb-content\"><div class=\"uagb-ifb-title-wrap\"><\/div><p class=\"uagb-ifb-desc\"><strong>Neu in Java 26<\/strong><br><br>Das Timeout-Verhalten l\u00e4sst sich seit Java 26 auch in eigenen Joinern steuern: Das <code>Joiner<\/code>-Interface hat daf\u00fcr die Methode <code>onTimeout()<\/code> bekommen, die beim Ablaufen des Timeouts aufgerufen wird. Bestehende Joiner, die keine Timeouts behandeln, musst du nicht anpassen \u2013 wie im <code>BestResultJoiner<\/code> oben implementierst du nur <code>onComplete()<\/code> und <code>result()<\/code>.<\/p><\/div><\/div>\n<\/div>\n\n\n\n<div class=\"wp-block-uagb-container uagb-block-f485bb74 alignfull uagb-is-root-container\">\n<div class=\"uagb-block-6feb3e50 uagb-infobox__content-wrap  uagb-infobox-icon-left uagb-infobox-left uagb-infobox-stacked-mobile uagb-infobox-image-valign-top \"><div class=\"uagb-ifb-icon-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z\"><\/path><\/svg><\/div><div class=\"uagb-ifb-content\"><div class=\"uagb-ifb-title-wrap\"><\/div><p class=\"uagb-ifb-desc\"><strong>Ausblick: Java 27<\/strong><br><br>In Java 27 kommt eine neue <code>open<\/code>-\u00dcberladung hinzu, die die Standard-Policy direkt mit einem Konfigurations-Operator kombiniert. Ein Timeout (oder einen Namen) f\u00fcr die Default-Policy setzt du dann, ohne einen Joiner \u00fcbergeben zu m\u00fcssen: <code>StructuredTaskScope.open(cf -&gt; cf.withTimeout(timeout))<\/code>.<\/p><\/div><\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"verschachtelte-structuredtaskscopes\">Verschachtelte StructuredTaskScopes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Falls wir nicht nur die Lieferanten f\u00fcr ein Produkt gleichzeitig abfragen m\u00f6chten, sondern die Lieferanten f\u00fcr mehrere Produkte, l\u00f6sen wir das ganz einfach mit verschachtelten Scopes (Klasse <a href=\"https:\/\/github.com\/SvenWoltmann\/structured-concurrency\/blob\/main\/src\/main\/java\/eu\/happycoders\/structuredconcurrency\/demo3_suppliers\/SupplierDeliveryTimeCheck3_NestedStructuredTaskScope.java\" target=\"_blank\" rel=\"noopener\">SupplierDeliveryTimeCheck3_NestedStructuredTaskScope<\/a> in der Demo-Anwendung). Dieses Mal verwenden wir <code>Joiner.allSuccessfulOrThrow()<\/code>, um von <code>scope.join()<\/code> direkt eine Liste der Ergebnisse zu bekommen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-function\">List&lt;SupplierDeliveryTime&gt; <span class=\"hljs-title\">getSupplierDeliveryTimes<\/span><span class=\"hljs-params\">(\n        List&lt;String&gt; productIds, List&lt;String&gt; supplierIds)<\/span> <span class=\"hljs-keyword\">throws<\/span> InterruptedException <\/span>{\n    <span class=\"hljs-keyword\">try<\/span> (<span class=\"hljs-keyword\">var<\/span> scope = StructuredTaskScope.open(\n            Joiner.&lt;SupplierDeliveryTime&gt;allSuccessfulOrThrow())) {\n        productIds.forEach(productId -&gt; \n                scope.fork(() -&gt; getSupplierDeliveryTime(productId, supplierIds)));\n\n        <span class=\"hljs-keyword\">return<\/span> scope.join();\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div class=\"wp-block-uagb-container uagb-block-11fd82c5 alignfull uagb-is-root-container\">\n<div class=\"uagb-block-ccbffc75 uagb-infobox__content-wrap  uagb-infobox-icon-left uagb-infobox-left uagb-infobox-stacked-mobile uagb-infobox-image-valign-top \"><div class=\"uagb-ifb-icon-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z\"><\/path><\/svg><\/div><div class=\"uagb-ifb-content\"><div class=\"uagb-ifb-title-wrap\"><\/div><p class=\"uagb-ifb-desc\"><strong>Java 25 \u2192 Java 26<\/strong><br><br>In Java 25 lieferte <code>allSuccessfulOrThrow()<\/code> einen <code>Stream<\/code> der <code>Subtask<\/code>-Objekte \u2013 der R\u00fcckgabewert sah dort noch so aus: <code>return scope.join().map(Subtask::get).toList();<\/code>. Seit Java 26 gen\u00fcgt <code>return scope.join();<\/code>, weil <code>join()<\/code> direkt die Liste der Ergebnisse liefert.<\/p><\/div><\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Wir erzeugen einen <code>StructuredTaskScope<\/code> \u2013 und innerhalb dieses Scopes forken wir Subtasks, die wiederum die im vorherigen Abschnitt gezeigte Methode <code>getSupplierDeliveryTime(\u2026)<\/code> aufrufen. Diese \u00f6ffnet ihrerseits einen Scope, der damit innerhalb des Scopes von <code>getSupplierDeliveryTimes(\u2026)<\/code> verschachtelt ist.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die folgende Grafik zeigt diese Scopes als gestrichelte Linien:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"286\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-nested-800x286.png\" alt=\"Verschachtelte StructuredTaskScopes\" class=\"wp-image-37249\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-nested-800x286.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-nested-224x80.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-nested-336x120.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-nested-504x180.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-nested-672x240.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-nested-400x143.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-nested-600x215.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-nested-944x337.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-nested-1200x429.png 1200w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-nested.png 1600w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Verschachtelte StructuredTaskScopes<\/figcaption><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"vorteile-von-structured-concurrency\">Vorteile von Structured Concurrency<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Structured Concurrency zeichnet sich durch klar im Code ersichtliche Start- und Endpunkte nebenl\u00e4ufiger Subtasks aus. Fehler in den Subtasks werden an den Eltern-Scope propagiert. Das macht den Code besser les- und wartbar und stellt sicher, dass zum Ende eines Scopes alle gestarteten Threads beendet sind.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die folgende Grafik zeigt Unstructured und Structured Concurrency gegen\u00fcbergestellt:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"369\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-vs-unstructured-concurrency.v2-800x369.png\" alt=\"Unstructured Concurrency vs. Structured Concurrency\" class=\"wp-image-37255\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-vs-unstructured-concurrency.v2-800x369.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-vs-unstructured-concurrency.v2-224x103.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-vs-unstructured-concurrency.v2-336x155.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-vs-unstructured-concurrency.v2-504x232.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-vs-unstructured-concurrency.v2-672x310.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-vs-unstructured-concurrency.v2-400x185.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-vs-unstructured-concurrency.v2-600x277.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-vs-unstructured-concurrency.v2-944x435.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-vs-unstructured-concurrency.v2-1200x554.png 1200w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/structured-concurrency-vs-unstructured-concurrency.v2.png 1600w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Unstructured Concurrency vs. Structured Concurrency<\/figcaption><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"vorteile-von-structuredtaskscope\">Vorteile von StructuredTaskScope<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Mit <code>StructuredTaskScope<\/code> haben wir ein Sprachkonstrukt f\u00fcr <em>Structured Concurrency<\/em>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Task und Subtasks bilden im Code eine abgeschlossene Einheit \u2013 es gibt keinen <code>ExecutorService<\/code> in einem h\u00f6heren Scope, wie z. B. dem der Klasse. Die Threads kommen nicht aus einem Threadpool; stattdessen wird jeder Subtask in einem neuen <a href=\"\/de\/java\/virtual-threads\/\">virtuellen Thread<\/a> ausgef\u00fchrt.<\/li>\n\n\n\n<li>Durch den mittels try-with-resources-Block aufgespannten Scope ergeben sich klare Start- und Endpunkte aller Threads.<\/li>\n\n\n\n<li>Am Ende des Scopes sind alle Threads beendet.<\/li>\n\n\n\n<li>Fehler innerhalb der Subtasks werden sauber an den Eltern-Scope propagiert.<\/li>\n\n\n\n<li>Je nach Policy werden die verbleibenden Subtasks abgebrochen, wenn ein Subtask erfolgreich war oder wenn in einem Subtask ein Fehler auftrat.<\/li>\n\n\n\n<li>Wenn der aufrufende Thread abgebrochen wird, werden auch die Subtasks abgebrochen.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Dar\u00fcber hinaus hilft <code>StructuredTaskScope<\/code> beim Debuggen: Wenn wir einen Thread-Dump im JSON-Format ausgeben (<code>jcmd &lt;pid&gt; Thread.dump_to_file -format=json &lt;file&gt;<\/code>), dann ist darin die Aufrufhierarchie zwischen Eltern- und Kind-Threads ersichtlich.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"structuredtaskscope-und-scoped-values\">StructuredTaskScope und Scoped Values<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Die seit <a href=\"https:\/\/www.happycoders.eu\/de\/java\/java-25-features\/#scoped-values-jep-506\">Java 25<\/a> finalisierten <a href=\"\/de\/java\/scoped-values\/\">Scoped Values<\/a> werden beim Einsatz von <code>StructuredTaskScope<\/code> innerhalb eines Scopes automatisch an alle durch <code>StructuredTaskScope.fork(\u2026)<\/code> erzeugten Kind-Threads weitervererbt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wie das genau funktioniert, zeige ich dir an folgendem Code-Beispiel (Klasse <a href=\"https:\/\/github.com\/SvenWoltmann\/structured-concurrency\/blob\/main\/src\/main\/java\/eu\/happycoders\/structuredconcurrency\/demo3_suppliers\/SupplierDeliveryTimeCheck4_NestedStructuredTaskScopeUsingScopedValue.java\" target=\"_blank\" rel=\"noopener\">SupplierDeliveryTimeCheck4_NestedStructuredTaskScopeUsingScopedValue<\/a> in der Demo-Anwendung).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wir erzeugen ein <code>ScopedValue<\/code> \u2013 im Beispiel f\u00fcr einen API-Key \u2013, binden dieses an den API-Key und rufen dann die im Abschnitt \u201eVerschachtelte StructuredTaskScopes\" gezeigte Methode <code>getSupplierDeliveryTimes(\u2026)<\/code> innerhalb des Scopes per <code>call()<\/code> auf:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">final<\/span> ScopedValue&lt;String&gt; API_KEY = ScopedValue.newInstance();\n\n<span class=\"hljs-function\">List&lt;SupplierDeliveryTime&gt; <span class=\"hljs-title\">getSupplierDeliveryTimes<\/span><span class=\"hljs-params\">(List&lt;String&gt; productIds, \n        List&lt;String&gt; supplierIds, String apiKey)<\/span> <span class=\"hljs-keyword\">throws<\/span> Exception <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> ScopedValue.where(API_KEY, apiKey)\n            .call(() -&gt; getSupplierDeliveryTimes(productIds, supplierIds));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Durch die Vererbung des Scoped Values <code>API_KEY<\/code> kann auf diesen auch innerhalb der <code>SupplierDeliveryTimeService.getDeliveryTime(<code>\u2026<\/code>)<\/code>-Methode zugegriffen werden, ohne ihn \u00fcber Methodenargumente durchschleifen zu m\u00fcssen \u2013 und das selbst dann, wenn die Methoden nicht in demjenigen Thread ausgef\u00fchrt werden, der <code>ScopedValue.where(<code>\u2026<\/code>)<\/code> aufruft, sondern in den durch <code>StructuredTaskScope.fork(<code>\u2026<\/code>)<\/code> erzeugten Kind- bzw. in diesem Beispiel sogar Enkel-Threads.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"historie\">Historie<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Structured Concurrency<\/em> wurde in folgenden JDK Enhancement Proposals definiert:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Java 19: <a href=\"https:\/\/openjdk.org\/jeps\/428\" target=\"_blank\" rel=\"noreferrer noopener\">JEP 428: Structured Concurrency (Incubator)<\/a><\/li>\n\n\n\n<li>Java 20: <a href=\"https:\/\/openjdk.org\/jeps\/437\" target=\"_blank\" rel=\"noreferrer noopener\">JEP 437: Structured Concurrency (Second Incubator)<\/a><\/li>\n\n\n\n<li>Java 21: <a href=\"https:\/\/openjdk.org\/jeps\/453\" target=\"_blank\" rel=\"noreferrer noopener\">JEP 453: Structured Concurrency (Preview)<\/a><\/li>\n\n\n\n<li>Java 22: <a href=\"https:\/\/openjdk.org\/jeps\/462\" target=\"_blank\" rel=\"noreferrer noopener\">JEP 462: Structured Concurrency (Second Preview)<\/a><\/li>\n\n\n\n<li>Java 23: <a href=\"https:\/\/openjdk.org\/jeps\/480\" target=\"_blank\" rel=\"noreferrer noopener\">JEP 480: Structured Concurrency (Third Preview)<\/a><\/li>\n\n\n\n<li>Java 24: <a href=\"https:\/\/openjdk.org\/jeps\/499\" target=\"_blank\" rel=\"noreferrer noopener\">JEP 499: Structured Concurrency (Fourth Preview)<\/a><\/li>\n\n\n\n<li>Java 25: <a href=\"https:\/\/openjdk.org\/jeps\/505\" target=\"_blank\" rel=\"noreferrer noopener\">JEP 505: Structured Concurrency (Fifth Preview)<\/a><\/li>\n\n\n\n<li>Java 26: <a href=\"https:\/\/openjdk.org\/jeps\/525\" target=\"_blank\" rel=\"noreferrer noopener\">JEP 525: Structured Concurrency (Sixth Preview)<\/a><\/li>\n\n\n\n<li>Java 27: <a href=\"https:\/\/openjdk.org\/jeps\/533\" target=\"_blank\" rel=\"noreferrer noopener\">JEP 533: Structured Concurrency (Seventh Preview)<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"fazit\">Fazit<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Structured Concurrency<\/em> vereinfacht \u2013 aufbauend auf <a href=\"https:\/\/www.happycoders.eu\/de\/java\/virtual-threads\/\">virtuellen Threads<\/a> \u2013 die Verwaltung von Tasks, die in nebenl\u00e4ufige Subtasks aufgeteilt werden, deutlich. Policies erlauben uns, das Verhalten von <code>StructuredTaskScope<\/code> zu beeinflussen, z. B. um alle Tasks abzubrechen, sollte einer fehlgeschlagen sein.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die API wurde in <a href=\"https:\/\/www.happycoders.eu\/de\/java\/java-25-features\/#structured-concurrency-fifth-preview-jep-505\">Java 25<\/a> grundlegend \u00fcberarbeitet: <code>StructuredTaskScope<\/code> und Join-Strategie wurden entkoppelt, was zu klarer strukturiertem, verst\u00e4ndlicherem und robusterem Code f\u00fchrt \u2013 Stichwort \u201e<a href=\"https:\/\/de.wikipedia.org\/wiki\/Komposition_an_Stelle_von_Vererbung\" target=\"_blank\" rel=\"noreferrer noopener\">Composition over inheritance<\/a>\u201c. In <a href=\"https:\/\/www.happycoders.eu\/de\/java\/java-26-features\/#structured-concurrency-sixth-preview-jep-525\">Java 26<\/a> (<a href=\"https:\/\/openjdk.org\/jeps\/525\">JEP 525<\/a>) kamen nur noch kleinere Anpassungen hinzu, auf die ich dich in den Infoboxen oben hingewiesen habe.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Bitte beachte, dass sich Structured Concurrency nach wie vor im Preview-Stadium befindet: in Java 26 als sechster Preview (JEP 525), in der kommenden Version <a href=\"https:\/\/www.happycoders.eu\/de\/java\/java-27-features\/#structured-concurrency-seventh-preview-jep-533\">Java 27<\/a> als siebter Preview (<a href=\"https:\/\/openjdk.org\/jeps\/533\">JEP 533<\/a>). Die API kann also noch \u00c4nderungen unterliegen. F\u00fcr Java 27 sind vor allem Verfeinerungen beim Exception-Handling vorgesehen: Die Standard-Joiner werfen dann eine <code>ExecutionException<\/code> statt einer <code>FailedException<\/code>, und ein neuer Typparameter macht den von <code>join()<\/code> geworfenen Exception-Typ explizit. Die Details findest du in den \u201eAusblick: Java 27\"-Infoboxen oben.<\/p>\n<aside><p>Wenn dir der Artikel weitergeholfen hat, w\u00fcrde ich mich sehr \u00fcber eine positive Bewertung auf meinem <a href=\"https:\/\/www.provenexpert.com\/de-de\/sven-woltmann-happycoders-eu\/7smk\/\" target=\"_blank\" rel=\"noopener\">ProvenExpert-Profil<\/a> freuen. Dein Feedback hilft mir, meine Inhalte weiter zu verbessern und motiviert mich, neue informative Artikel zu schreiben.<\/p>\r\n                        <p>\ud83d\udc49 <a href=\"https:\/\/www.provenexpert.com\/de-de\/sven-woltmann-happycoders-eu\/7smk\/\" target=\"_blank\" rel=\"noopener\">Bewertung abgeben<\/a><\/p>\r\n                        <p>Du m\u00f6chtest \u00fcber alle neuen Java-Features auf dem Laufenden sein? Dann <a href=\"#\" data-formkit-toggle=\"d8ee997126\">klicke hier<\/a>, um dich f\u00fcr den HappyCoders-Newsletter anzumelden.<\/p>\r\n                        <p>\ud83d\udc49 <a href=\"#\" data-formkit-toggle=\"d8ee997126\">Newsletter-Anmeldung<\/a><\/p><\/aside>","protected":false},"excerpt":{"rendered":"<p>Was ist Structured Concurrency und wof\u00fcr ben\u00f6tigen wir sie? Wie funktioniert StructuredTaskScope? Was ist der Vorteil von Structured Concurrency?<\/p>\n","protected":false},"author":1,"featured_media":34140,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_seopress_titles_title":"","_seopress_titles_desc":"Structured Concurrency macht Code lesbarer und robuster. Erfahre, wie StructuredTaskScope und Policies funktionieren \u2013 und wie du eigene schreibst.","_seopress_robots_index":"","_seopress_robots_follow":"","_seopress_robots_imageindex":"","_seopress_robots_snippet":"","_seopress_robots_primary_cat":"none","_seopress_robots_breadcrumbs":"","_seopress_robots_freeze_modified_date":"","_seopress_robots_custom_modified_date":"","_seopress_robots_canonical":"","_seopress_social_fb_title":"","_seopress_social_fb_desc":"","_seopress_social_fb_img":"","_seopress_social_fb_img_attachment_id":0,"_seopress_social_fb_img_width":0,"_seopress_social_fb_img_height":0,"_seopress_social_twitter_title":"","_seopress_social_twitter_desc":"","_seopress_social_twitter_img":"","_seopress_social_twitter_img_attachment_id":0,"_seopress_social_twitter_img_width":0,"_seopress_social_twitter_img_height":0,"_seopress_redirections_value":"","_seopress_redirections_enabled":"","_seopress_redirections_enabled_regex":"","_seopress_redirections_logged_status":"both","_seopress_redirections_param":"","_seopress_redirections_type":301,"_seopress_analysis_target_kw":"structured concurrency,structuredtaskscope","_seopress_news_disabled":"","_seopress_video_disabled":"","_seopress_video":[],"_seopress_pro_schemas_manual":[{"_seopress_pro_rich_snippets_type":"none"}],"_seopress_pro_rich_snippets_disable_all":"","_seopress_pro_rich_snippets_disable":[],"_seopress_pro_schemas":[],"_uag_custom_page_level_css":"","_wp_convertkit_post_meta":{"form":"-1","landing_page":"","tag":"0","restrict_content":"0"},"_metis_text_type":"standard","_metis_text_length":27511,"_post_count":0,"footnotes":""},"categories":[64],"tags":[220],"class_list":["post-34113","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-java","tag-java-nebenlaeufigkeit"],"uagb_featured_image_src":{"full":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java.jpg",1770,986,false],"thumbnail":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java.jpg",150,84,false],"medium":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java.jpg",300,167,false],"medium_large":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java.jpg",768,428,false],"large":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java.jpg",1024,570,false],"feature_thumb_224":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java-224x125.jpg",224,125,true],"feature_thumb_336":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java-336x187.jpg",336,187,true],"feature_thumb_504":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java-504x281.jpg",504,281,true],"feature_thumb_672":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java-672x374.jpg",672,374,true],"half_400":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java-400x223.jpg",400,223,true],"half_600":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java-600x334.jpg",600,334,true],"full_800":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java-800x446.jpg",800,446,true],"full_944":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java-944x526.jpg",944,526,true],"full_1200":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java-1200x668.jpg",1200,668,true],"wide_1180":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java-1180x490.jpg",1180,490,true],"wide_1770":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java-1770x735.jpg",1770,735,true],"1536x1536":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java.jpg",1536,856,false],"2048x2048":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2022\/12\/structured-concurrency-java.jpg",1770,986,false]},"uagb_author_info":{"display_name":"Sven Woltmann","author_link":"https:\/\/www.happycoders.eu\/de\/author\/sven\/"},"uagb_comment_info":0,"uagb_excerpt":"Was ist Structured Concurrency und wof\u00fcr ben\u00f6tigen wir sie? Wie funktioniert StructuredTaskScope? Was ist der Vorteil von Structured Concurrency?","public_identification_id":"1f23a8ab4e1f4bdba5baabd231f0b8fd","private_identification_id":"18f33255e98d422a9745bb9dc7961fec","_links":{"self":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/34113","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/comments?post=34113"}],"version-history":[{"count":26,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/34113\/revisions"}],"predecessor-version":[{"id":56569,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/34113\/revisions\/56569"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/media\/34140"}],"wp:attachment":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/media?parent=34113"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/categories?post=34113"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/tags?post=34113"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}