{"id":55,"date":"2023-03-01T15:40:25","date_gmt":"2023-03-01T15:40:25","guid":{"rendered":"https:\/\/java-online-training.de\/?p=55"},"modified":"2023-03-19T08:36:10","modified_gmt":"2023-03-19T08:36:10","slug":"java-19-structured-concurrency","status":"publish","type":"post","link":"https:\/\/java-online-training.de\/?p=55","title":{"rendered":"Java 19: Structured Concurrency"},"content":{"rendered":"\n<p>Another very interesting feature that will most likely be available with Java 21 is the introduction of the <strong>Structured Concurrency<\/strong> API developed under <a href=\"https:\/\/openjdk.org\/jeps\/428\" data-type=\"URL\" data-id=\"https:\/\/openjdk.org\/jeps\/428\">JEP 428<\/a> by Project Loom. Structured concurrency will greatly simplify the use of virtual threads and provide an API similar to the well know <em>ExecutorService <\/em>API to execute <em>Callables<\/em>. If you haven&#8217;t heard about virtual threads yet, check out this <a href=\"https:\/\/java-online-training.de\/?p=3\" data-type=\"post\" data-id=\"3\">blog post <\/a>which explains the pros and cons of virtual vs platform threads. Structured Concurrency is currently available as an incubator feature in Java 19. <\/p>\n\n\n\n<p>The centerpiece of the new API is the class <em>StructuredTaskScope<\/em>, which is used to start a virtual thread with a given <em>Callable <\/em>to execute.  In exchange you get a <em>Future <\/em>that provides access to the result of the <em>Callable <\/em>after it finished. Here is simple example of <em>StructuredTaskScope <\/em>in action:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" style=\"font-size:1rem;--cbp-line-number-color:#adbac7;--cbp-line-number-width:17.59375px;line-height:1.625rem\"><span role=\"button\" tabindex=\"0\" data-code=\"try (var scope = new StructuredTaskScope&lt;String&gt;()){\n\n  Callable&lt;String&gt; callable = () -&gt; &quot;Running ... Finished!&quot;;\n\n  Future&lt;String&gt; result = scope.fork(callable);\n            \n  scope.join();\n            \n  System.out.println(result.get());\n}\n\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\"><code><span class=\"line\"><span style=\"color: #F47067\">try<\/span><span style=\"color: #ADBAC7\"> (<\/span><span style=\"color: #F47067\">var<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #ADBAC7\">scope<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">new<\/span><span style=\"color: #ADBAC7\"> StructuredTaskScope&lt;<\/span><span style=\"color: #F47067\">String<\/span><span style=\"color: #ADBAC7\">&gt;()){<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">  Callable<\/span><span style=\"color: #F69D50\">&lt;<\/span><span style=\"color: #F47067\">String<\/span><span style=\"color: #F69D50\">&gt; <\/span><span style=\"color: #ADBAC7\">callable<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> () <\/span><span style=\"color: #F47067\">-&gt;<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #96D0FF\">&quot;Running ... Finished!&quot;<\/span><span style=\"color: #ADBAC7\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">  Future<\/span><span style=\"color: #F69D50\">&lt;<\/span><span style=\"color: #F47067\">String<\/span><span style=\"color: #F69D50\">&gt; <\/span><span style=\"color: #ADBAC7\">result<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> scope.<\/span><span style=\"color: #DCBDFB\">fork<\/span><span style=\"color: #ADBAC7\">(callable);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            <\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">  scope.<\/span><span style=\"color: #DCBDFB\">join<\/span><span style=\"color: #ADBAC7\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            <\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">  System.out.<\/span><span style=\"color: #DCBDFB\">println<\/span><span style=\"color: #ADBAC7\">(result.<\/span><span style=\"color: #DCBDFB\">get<\/span><span style=\"color: #ADBAC7\">());<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">}<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<p>In line (1) I create a new <em>StructuredTaskScope <\/em>in a try-with-resource block. <em>StructuredTaskScope <\/em>implements the interface <em>AutoClosable <\/em>and we can just conveniently close it like this after use. In line (3) and (5), I create a <em>Callable <\/em>and start it with the <em>fork<\/em><strong> <\/strong>method of <em>StructuredTaskScope<\/em>. Finally, I wait for the <em>Callable <\/em>to finish in line (7) by invoking the <em>join <\/em>method. The code blocks in line (7) until the <em>Callable <\/em>is completed and then resumes by printing the result into the console.<\/p>\n\n\n\n<p>On short note on running the above code yourself. Since structured concurrency is an<strong> incubator feature<\/strong>, you won&#8217;t be able to just run it with Java 19 installed. You will need to explicitly activate <strong>preview features<\/strong> and the <strong>concurrency incubator feature<\/strong> when you run your program:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" style=\"font-size:1rem;--cbp-line-number-color:#adbac7;--cbp-line-number-width:8.796875px;line-height:1.625rem\"><span role=\"button\" tabindex=\"0\" data-code=\"java --enable-preview --add-modules=jdk.incubator.concurrent a.b.Example\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\"><code><span class=\"line\"><span style=\"color: #ADBAC7\">java <\/span><span style=\"color: #F47067\">--<\/span><span style=\"color: #ADBAC7\">enable<\/span><span style=\"color: #F47067\">-<\/span><span style=\"color: #ADBAC7\">preview <\/span><span style=\"color: #F47067\">--<\/span><span style=\"color: #ADBAC7\">add<\/span><span style=\"color: #F47067\">-<\/span><span style=\"color: #ADBAC7\">modules<\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\">jdk.incubator.concurrent a.b.Example<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>How to make a Maven project that uses preview features to compile your code<a href=\"https:\/\/java-online-training.de\/?page_id=83\" data-type=\"page\" data-id=\"83\"> can be found here<\/a>.<\/p>\n\n\n\n<p>Let&#8217;s expand our code a little bit to see multiple virtual threads running at the same time.  As a simple business case, I want to call a service that returns currency conversion rates from Euros into a bunch of other currencies. The service has a slight delay build in to simulate a slow operation to query the exchange rate, e.g. a web service call or database access. Since I want to keep this example simple, I will just stop the thread for 500 milliseconds instead of implementing a real web service or database access. Here is the code for the currency conversion rate service:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" style=\"font-size:.875rem;--cbp-line-number-color:#adbac7;--cbp-line-number-width:15.40625px;line-height:1.625rem\"><span role=\"button\" tabindex=\"0\" data-code=\"public class CurrencyConversionService {\n\n    public Double getConvertionRate(Currency currency) throws Exception {\n        Thread.sleep(500l);\n        return currency.getRate();\n    }\n\n    public ConversionResult getEuroConvertionResult(Double amount, Currency target) throws Exception {\n        return new \n          ConversionResult(\n            Double.valueOf(amount*getConvertionRate(target)),\n            target,\n            amount);\n    }\n\n    public enum Currency { \n    \n        USD(1.2d) , EUR(1d) , JPY(144d) , CAD(1.44d) , GBP(0.88d); \n    \n        private Double rate;\n\n        private Currency(Double rate){ this.rate = rate;}\n\n        public Double getRate(){ return rate; }\n    };\n}\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\"><code><span class=\"line\"><span style=\"color: #F47067\">public<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">class<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F69D50\">CurrencyConversionService<\/span><span style=\"color: #ADBAC7\"> {<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #F47067\">public<\/span><span style=\"color: #ADBAC7\"> Double <\/span><span style=\"color: #DCBDFB\">getConvertionRate<\/span><span style=\"color: #ADBAC7\">(Currency <\/span><span style=\"color: #F69D50\">currency<\/span><span style=\"color: #ADBAC7\">) <\/span><span style=\"color: #F47067\">throws<\/span><span style=\"color: #ADBAC7\"> Exception {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        Thread.<\/span><span style=\"color: #DCBDFB\">sleep<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #6CB6FF\">500l<\/span><span style=\"color: #ADBAC7\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #F47067\">return<\/span><span style=\"color: #ADBAC7\"> currency.<\/span><span style=\"color: #DCBDFB\">getRate<\/span><span style=\"color: #ADBAC7\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #F47067\">public<\/span><span style=\"color: #ADBAC7\"> ConversionResult <\/span><span style=\"color: #DCBDFB\">getEuroConvertionResult<\/span><span style=\"color: #ADBAC7\">(Double <\/span><span style=\"color: #F69D50\">amount<\/span><span style=\"color: #ADBAC7\">, Currency <\/span><span style=\"color: #F69D50\">target<\/span><span style=\"color: #ADBAC7\">) <\/span><span style=\"color: #F47067\">throws<\/span><span style=\"color: #ADBAC7\"> Exception {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #F47067\">return<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">new<\/span><span style=\"color: #ADBAC7\"> <\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">          <\/span><span style=\"color: #DCBDFB\">ConversionResult<\/span><span style=\"color: #ADBAC7\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            Double.<\/span><span style=\"color: #DCBDFB\">valueOf<\/span><span style=\"color: #ADBAC7\">(amount<\/span><span style=\"color: #F47067\">*<\/span><span style=\"color: #DCBDFB\">getConvertionRate<\/span><span style=\"color: #ADBAC7\">(target)),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            target,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            amount);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #F47067\">public<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">enum<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F69D50\">Currency<\/span><span style=\"color: #ADBAC7\"> { <\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #6CB6FF\">USD<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #6CB6FF\">1.2d<\/span><span style=\"color: #ADBAC7\">) , <\/span><span style=\"color: #6CB6FF\">EUR<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #6CB6FF\">1d<\/span><span style=\"color: #ADBAC7\">) , <\/span><span style=\"color: #6CB6FF\">JPY<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #6CB6FF\">144d<\/span><span style=\"color: #ADBAC7\">) , <\/span><span style=\"color: #6CB6FF\">CAD<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #6CB6FF\">1.44d<\/span><span style=\"color: #ADBAC7\">) , <\/span><span style=\"color: #6CB6FF\">GBP<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #6CB6FF\">0.88d<\/span><span style=\"color: #ADBAC7\">); <\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #F47067\">private<\/span><span style=\"color: #ADBAC7\"> Double<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #ADBAC7\">rate;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #F47067\">private<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">Currency<\/span><span style=\"color: #ADBAC7\">(Double <\/span><span style=\"color: #F69D50\">rate<\/span><span style=\"color: #ADBAC7\">){ <\/span><span style=\"color: #6CB6FF\">this<\/span><span style=\"color: #ADBAC7\">.rate <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> rate;}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #F47067\">public<\/span><span style=\"color: #ADBAC7\"> Double <\/span><span style=\"color: #DCBDFB\">getRate<\/span><span style=\"color: #ADBAC7\">(){ <\/span><span style=\"color: #F47067\">return<\/span><span style=\"color: #ADBAC7\"> rate; }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    };<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The supported currencies including the rates are hardcoded in the <em>Currency <\/em>enumeration to keep things simple.  There are two business methods in this service, one that returns the conversion rate (<em>getConvertionRate<\/em>) and the second that converts a given amount in Euros to another currency (<em>getEuroConvertionResult<\/em>).  The second method returns a <em>ConversionResult<\/em> object, which is a simple record that stores the result of the conversion, the currency and the original amount in Euros. We will use the second method later in this post.<\/p>\n\n\n\n<p>I will now expand the first example, so that the service class is called for all available conversion rates in a virtual thread and collect the results into a HashMap. <\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" style=\"font-size:.875rem;--cbp-line-number-color:#adbac7;--cbp-line-number-width:15.40625px;line-height:1.25rem\"><span role=\"button\" tabindex=\"0\" data-code=\" CurrencyConversionService currencyService = new CurrencyConversionService();\n \n try(var scope = new StructuredTaskScope&lt;Double&gt;()){\n\n   Map&lt;Currency,Future&lt;Double&gt;&gt; results = new HashMap&lt;&gt;();\n\n   results.put(USD, scope.fork( () -&gt; currencyService.getConvertionRate(USD) ));\n   results.put(JPY, scope.fork( () -&gt; currencyService.getConvertionRate(JPY) ));\n   results.put(EUR, scope.fork( () -&gt; currencyService.getConvertionRate(EUR) ));\n   results.put(CAD, scope.fork( () -&gt; currencyService.getConvertionRate(CAD) ));\n   results.put(GBP, scope.fork( () -&gt; currencyService.getConvertionRate(GBP) ));\n\n   scope.join();\n\n   for(var currency : results.keySet()) {\n     System.out.println(&quot;Conversion rate for &quot;+ currency + &quot; is &quot; +\n       results.get(currency).get() );\n   }\n }\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\"><code><span class=\"line\"><span style=\"color: #ADBAC7\"> CurrencyConversionService<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #ADBAC7\">currencyService<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">new<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">CurrencyConversionService<\/span><span style=\"color: #ADBAC7\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\"> <\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">try<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #F47067\">var<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #ADBAC7\">scope<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">new<\/span><span style=\"color: #ADBAC7\"> StructuredTaskScope&lt;<\/span><span style=\"color: #F47067\">Double<\/span><span style=\"color: #ADBAC7\">&gt;()){<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">   Map<\/span><span style=\"color: #F69D50\">&lt;<\/span><span style=\"color: #F47067\">Currency<\/span><span style=\"color: #F69D50\">,<\/span><span style=\"color: #ADBAC7\">Future<\/span><span style=\"color: #F69D50\">&lt;<\/span><span style=\"color: #F47067\">Double<\/span><span style=\"color: #F69D50\">&gt;&gt; <\/span><span style=\"color: #ADBAC7\">results<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">new<\/span><span style=\"color: #ADBAC7\"> HashMap&lt;&gt;();<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">   results.<\/span><span style=\"color: #DCBDFB\">put<\/span><span style=\"color: #ADBAC7\">(USD, scope.<\/span><span style=\"color: #DCBDFB\">fork<\/span><span style=\"color: #ADBAC7\">( () <\/span><span style=\"color: #F47067\">-&gt;<\/span><span style=\"color: #ADBAC7\"> currencyService.<\/span><span style=\"color: #DCBDFB\">getConvertionRate<\/span><span style=\"color: #ADBAC7\">(USD) ));<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">   results.<\/span><span style=\"color: #DCBDFB\">put<\/span><span style=\"color: #ADBAC7\">(JPY, scope.<\/span><span style=\"color: #DCBDFB\">fork<\/span><span style=\"color: #ADBAC7\">( () <\/span><span style=\"color: #F47067\">-&gt;<\/span><span style=\"color: #ADBAC7\"> currencyService.<\/span><span style=\"color: #DCBDFB\">getConvertionRate<\/span><span style=\"color: #ADBAC7\">(JPY) ));<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">   results.<\/span><span style=\"color: #DCBDFB\">put<\/span><span style=\"color: #ADBAC7\">(EUR, scope.<\/span><span style=\"color: #DCBDFB\">fork<\/span><span style=\"color: #ADBAC7\">( () <\/span><span style=\"color: #F47067\">-&gt;<\/span><span style=\"color: #ADBAC7\"> currencyService.<\/span><span style=\"color: #DCBDFB\">getConvertionRate<\/span><span style=\"color: #ADBAC7\">(EUR) ));<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">   results.<\/span><span style=\"color: #DCBDFB\">put<\/span><span style=\"color: #ADBAC7\">(CAD, scope.<\/span><span style=\"color: #DCBDFB\">fork<\/span><span style=\"color: #ADBAC7\">( () <\/span><span style=\"color: #F47067\">-&gt;<\/span><span style=\"color: #ADBAC7\"> currencyService.<\/span><span style=\"color: #DCBDFB\">getConvertionRate<\/span><span style=\"color: #ADBAC7\">(CAD) ));<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">   results.<\/span><span style=\"color: #DCBDFB\">put<\/span><span style=\"color: #ADBAC7\">(GBP, scope.<\/span><span style=\"color: #DCBDFB\">fork<\/span><span style=\"color: #ADBAC7\">( () <\/span><span style=\"color: #F47067\">-&gt;<\/span><span style=\"color: #ADBAC7\"> currencyService.<\/span><span style=\"color: #DCBDFB\">getConvertionRate<\/span><span style=\"color: #ADBAC7\">(GBP) ));<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">   scope.<\/span><span style=\"color: #DCBDFB\">join<\/span><span style=\"color: #ADBAC7\">();<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">   <\/span><span style=\"color: #F47067\">for<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #F47067\">var<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #ADBAC7\">currency<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #F47067\">:<\/span><span style=\"color: #ADBAC7\"> results.<\/span><span style=\"color: #DCBDFB\">keySet<\/span><span style=\"color: #ADBAC7\">()) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">     System.out.<\/span><span style=\"color: #DCBDFB\">println<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #96D0FF\">&quot;Conversion rate for &quot;<\/span><span style=\"color: #F47067\">+<\/span><span style=\"color: #ADBAC7\"> currency <\/span><span style=\"color: #F47067\">+<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #96D0FF\">&quot; is &quot;<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">+<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">       results.<\/span><span style=\"color: #DCBDFB\">get<\/span><span style=\"color: #ADBAC7\">(currency).<\/span><span style=\"color: #DCBDFB\">get<\/span><span style=\"color: #ADBAC7\">() );<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">   }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\"> }<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The main difference to our first example is, that I am creating five <em>Callables <\/em>and start each with the <em>fork <\/em>method of the <em>StructuredTaskScope<\/em> in line (7) to (11). The, I call the  <em>join <\/em>on the <em>StructuredTaskScope <\/em>to wait until all tasks are done in line (13).   All Futures are stored in a HashMap and after all tasks finished, I can print the results to the console with the the currency as the key and the result as the entry. <\/p>\n\n\n\n<p>Now you might think why this code is so special compared to the use of a regular ExecutorService to start <em>Callables<\/em>. First thing to mention is, that creating a <em>ExecutorService <\/em>is a very costly operation and <em>ExecutorServices<\/em> are usually created as a singleton. So creating it locally like in line (3) is a no-go looking at performance. The second point is, that using an <em>ExecutorService <\/em>to start a couple of blocking operations is also not the greatest idea, since the amount of platform threads you can have at a any given time is quite limited. Each platform thread has its on stack trace in memory and also needs some memory to do its job. The wise people that write Apache Tomcat picked 200 threads as the default for a Tomcat installation and this is actually a good number in many cases. If you run the code above on Apache Tomcat with an <em>ExecutorService <\/em>instead of a <em>StructuredTaskScope<\/em>, the server would become unresponsive if you started the code  more than 40 times.<\/p>\n\n\n\n<p>However, by using <em>StructuredTaskScope <\/em>and virtual threads, the server will never become unresponsive because it runs out of threads. Virtual threads are very lightweight compared to platform threads and blocking them is quite cheap, since the underlying platform thread that executes the virtual thread will simply work on another virtual thread in the meantime.  You can easily start a million virtual threads without breaking your application. This is the beauty when using virtual threads to execute business logic concurrently. You don&#8217;t need to think about shared thread pools and <em>ExecutorServices <\/em>anymore. You simply create as many virtual threads as you need by a local <em>StructuredTaskScope <\/em>and your application will run just fine. No more worrying about thread starvation because the virtual thread will make sure that your platform threads can be as productive as possible without you optimizing it.  <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Subclassing StructuredTaskScope<\/h4>\n\n\n\n<p>One thing that could be improved in the code of the last example, is the use of a HashMap to store the results of our <em>Callables<\/em>. It would be much nicer, if we could simply start all <em>Callables <\/em>and get the results back from the <em>StructuredTaskScope<\/em> and the API has a nice callback to achieve that.  You can override the <em>handleComplete <\/em>method which gets called when a Callable completed and is passed the corresponding Future. In this method you can manipulate the result or store it somewhere for easy access.  <\/p>\n\n\n\n<p>Subclassing <em>StructuredTaskScope <\/em>will look like this:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" style=\"font-size:.875rem;--cbp-line-number-color:#adbac7;--cbp-line-number-width:15.40625px;line-height:1.25rem\"><span role=\"button\" tabindex=\"0\" data-code=\"public class CurrencyConverterScope extends StructuredTaskScope&lt;ConversionResult&gt; {\n\n    Queue&lt;ConversionResult&gt; queue = new ConcurrentLinkedQueue&lt;ConversionResult&gt;();\n\n    @Override\n    protected void handleComplete(Future&lt;ConversionResult&gt; future) {\n        if (future.isDone()){\n            try {\n                queue.add(future.get());\n            } catch (Exception e) {\n                System.out.println(&quot;Exception converting result.&quot;);\n            }\n        }\n    }\n\n    public Queue&lt;ConversionResult&gt; getResults(){ return queue;}\n}\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\"><code><span class=\"line\"><span style=\"color: #F47067\">public<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">class<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F69D50\">CurrencyConverterScope<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">extends<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #6CB6FF\">StructuredTaskScope<\/span><span style=\"color: #ADBAC7\">&lt;<\/span><span style=\"color: #F47067\">ConversionResult<\/span><span style=\"color: #ADBAC7\">&gt; {<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    Queue<\/span><span style=\"color: #F69D50\">&lt;<\/span><span style=\"color: #F47067\">ConversionResult<\/span><span style=\"color: #F69D50\">&gt; <\/span><span style=\"color: #ADBAC7\">queue<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">new<\/span><span style=\"color: #ADBAC7\"> ConcurrentLinkedQueue&lt;<\/span><span style=\"color: #F47067\">ConversionResult<\/span><span style=\"color: #ADBAC7\">&gt;();<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    @<\/span><span style=\"color: #F47067\">Override<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #F47067\">protected<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">void<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">handleComplete<\/span><span style=\"color: #ADBAC7\">(Future&lt;<\/span><span style=\"color: #F47067\">ConversionResult<\/span><span style=\"color: #ADBAC7\">&gt; <\/span><span style=\"color: #F69D50\">future<\/span><span style=\"color: #ADBAC7\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #F47067\">if<\/span><span style=\"color: #ADBAC7\"> (future.<\/span><span style=\"color: #DCBDFB\">isDone<\/span><span style=\"color: #ADBAC7\">()){<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            <\/span><span style=\"color: #F47067\">try<\/span><span style=\"color: #ADBAC7\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                queue.<\/span><span style=\"color: #DCBDFB\">add<\/span><span style=\"color: #ADBAC7\">(future.<\/span><span style=\"color: #DCBDFB\">get<\/span><span style=\"color: #ADBAC7\">());<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            } <\/span><span style=\"color: #F47067\">catch<\/span><span style=\"color: #ADBAC7\"> (Exception <\/span><span style=\"color: #F69D50\">e<\/span><span style=\"color: #ADBAC7\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                System.out.<\/span><span style=\"color: #DCBDFB\">println<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #96D0FF\">&quot;Exception converting result.&quot;<\/span><span style=\"color: #ADBAC7\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #F47067\">public<\/span><span style=\"color: #ADBAC7\"> Queue&lt;<\/span><span style=\"color: #F47067\">ConversionResult<\/span><span style=\"color: #ADBAC7\">&gt; <\/span><span style=\"color: #DCBDFB\">getResults<\/span><span style=\"color: #ADBAC7\">(){ <\/span><span style=\"color: #F47067\">return<\/span><span style=\"color: #ADBAC7\"> queue;}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>In line (1), I create a subclass of a <em>StructuredTaskScope <\/em>to later override the <em>handleComplete <\/em>method. As the generic, we use the specialized record <em>ConversionResult<\/em>, which gives us a way to store the result in a structured way. In line (3), I define a <em>ConcurrentLinkedQueue <\/em>to later store the result in. This needs to be a concurrent data structure, because some <em>Callables <\/em>might complete at the same time. If you used a regular <em>ArrayList <\/em>here, you might get the infamous <em>ConcurrentModificationException <\/em>if two threads inserted an element into the list at the same time. From line (5) to (14) I override the <em>handleComplete <\/em>method and put the result into the queue. Finally, there is a getter for the results queue in line (16). <\/p>\n\n\n\n<p>Let&#8217;s modify our example to use the <em>CurrencyConverterScope <\/em>and the <em>ConversionResult<\/em>. I will also use the second business method of our converter service, which already uses the <em>ConversionResult<\/em>.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" style=\"font-size:.875rem;--cbp-line-number-color:#adbac7;--cbp-line-number-width:15.40625px;line-height:1.25rem\"><span role=\"button\" tabindex=\"0\" data-code=\"try(var scope = new CurrencyConverterScope()){\n    scope.fork( () -&gt; currencyService.getEuroConvertionResult(100d,USD) );\n    scope.fork( () -&gt; currencyService.getEuroConvertionResult(100d,JPY) );\n    scope.fork( () -&gt; currencyService.getEuroConvertionResult(100d,EUR) );\n    scope.fork( () -&gt; currencyService.getEuroConvertionResult(100d,CAD) );\n    scope.fork( () -&gt; currencyService.getEuroConvertionResult(100d,GBP) );\n\n    scope.join();\n\n    for(var result : scope.getResults()) {\n        System.out.println(result);\n    }\n}\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\"><code><span class=\"line\"><span style=\"color: #F47067\">try<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #F47067\">var<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #ADBAC7\">scope<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">new<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">CurrencyConverterScope<\/span><span style=\"color: #ADBAC7\">()){<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    scope.<\/span><span style=\"color: #DCBDFB\">fork<\/span><span style=\"color: #ADBAC7\">( () <\/span><span style=\"color: #F47067\">-&gt;<\/span><span style=\"color: #ADBAC7\"> currencyService.<\/span><span style=\"color: #DCBDFB\">getEuroConvertionResult<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #6CB6FF\">100d<\/span><span style=\"color: #ADBAC7\">,USD) );<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    scope.<\/span><span style=\"color: #DCBDFB\">fork<\/span><span style=\"color: #ADBAC7\">( () <\/span><span style=\"color: #F47067\">-&gt;<\/span><span style=\"color: #ADBAC7\"> currencyService.<\/span><span style=\"color: #DCBDFB\">getEuroConvertionResult<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #6CB6FF\">100d<\/span><span style=\"color: #ADBAC7\">,JPY) );<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    scope.<\/span><span style=\"color: #DCBDFB\">fork<\/span><span style=\"color: #ADBAC7\">( () <\/span><span style=\"color: #F47067\">-&gt;<\/span><span style=\"color: #ADBAC7\"> currencyService.<\/span><span style=\"color: #DCBDFB\">getEuroConvertionResult<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #6CB6FF\">100d<\/span><span style=\"color: #ADBAC7\">,EUR) );<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    scope.<\/span><span style=\"color: #DCBDFB\">fork<\/span><span style=\"color: #ADBAC7\">( () <\/span><span style=\"color: #F47067\">-&gt;<\/span><span style=\"color: #ADBAC7\"> currencyService.<\/span><span style=\"color: #DCBDFB\">getEuroConvertionResult<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #6CB6FF\">100d<\/span><span style=\"color: #ADBAC7\">,CAD) );<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    scope.<\/span><span style=\"color: #DCBDFB\">fork<\/span><span style=\"color: #ADBAC7\">( () <\/span><span style=\"color: #F47067\">-&gt;<\/span><span style=\"color: #ADBAC7\"> currencyService.<\/span><span style=\"color: #DCBDFB\">getEuroConvertionResult<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #6CB6FF\">100d<\/span><span style=\"color: #ADBAC7\">,GBP) );<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    scope.<\/span><span style=\"color: #DCBDFB\">join<\/span><span style=\"color: #ADBAC7\">();<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #F47067\">for<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #F47067\">var<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #ADBAC7\">result<\/span><span style=\"color: #F69D50\"> <\/span><span style=\"color: #F47067\">:<\/span><span style=\"color: #ADBAC7\"> scope.<\/span><span style=\"color: #DCBDFB\">getResults<\/span><span style=\"color: #ADBAC7\">()) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        System.out.<\/span><span style=\"color: #DCBDFB\">println<\/span><span style=\"color: #ADBAC7\">(result);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The code looks much cleaner like this. We use the second method of our conversion service to store the result and input parameters in a <em>ConversionResult<\/em>, which are later returned by our <em>Futures<\/em>. All <em>ConversionResults <\/em>are available by the <em>getResults <\/em>method of our custom scope.  This way we encapsulated all logic concerning the threading and result processing inside the <em>CurrencyConverterScope<\/em>. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Conclusion<\/h4>\n\n\n\n<p>That&#8217;s all I want to cover about structured concurrency for today. I think it will provide a nice API to write easy to understand and clean code once it is fully released. It will make concurrent coding a lot more accessible and many drawbacks of the old platform thread model are simply resolved in the background for you when using virtual threads.  There are also quite nice callback methods you can override to encapsulate your result processing and provide results in a convenient way. <\/p>\n\n\n\n<p>If you want to check out the code examples, you can find them as always in my <a href=\"https:\/\/github.com\/java-online-training\/java-19-structured-concurrency\" data-type=\"URL\" data-id=\"https:\/\/github.com\/java-online-training\/java-19-structured-concurrency\">github repository<\/a>. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Another very interesting feature that will most likely be available with Java 21 is the introduction of the Structured Concurrency API developed under JEP 428 by Project Loom. Structured concurrency will greatly simplify the use of virtual threads and provide an API similar to the well know ExecutorService API to execute Callables. If you haven&#8217;t [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[9],"tags":[11,4,5,6],"class_list":["post-55","post","type-post","status-publish","format-standard","hentry","category-java-19","tag-incubator","tag-java-19","tag-project-loom","tag-virtual-threads"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/java-online-training.de\/index.php?rest_route=\/wp\/v2\/posts\/55","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/java-online-training.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/java-online-training.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/java-online-training.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/java-online-training.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=55"}],"version-history":[{"count":35,"href":"https:\/\/java-online-training.de\/index.php?rest_route=\/wp\/v2\/posts\/55\/revisions"}],"predecessor-version":[{"id":281,"href":"https:\/\/java-online-training.de\/index.php?rest_route=\/wp\/v2\/posts\/55\/revisions\/281"}],"wp:attachment":[{"href":"https:\/\/java-online-training.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=55"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/java-online-training.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=55"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/java-online-training.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=55"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}