{"id":57370,"date":"2023-05-15T16:19:55","date_gmt":"2023-05-15T10:49:55","guid":{"rendered":"https:\/\/www.tothenew.com\/blog\/?p=57370"},"modified":"2023-09-06T16:21:23","modified_gmt":"2023-09-06T10:51:23","slug":"structured-task-scope-new-concurrency-model-in-jvm","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/structured-task-scope-new-concurrency-model-in-jvm\/","title":{"rendered":"Structured Task Scope &#8211; New Concurrency Model in JVM"},"content":{"rendered":"<p id=\"9311\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">Since the advent of virtual threads in Java, we have had a new and nice concurrency model introduced as\u00a0<strong class=\"sa ep\">StructuredTaskScope<\/strong>. We will see some patterns in this new model.<\/p>\n<p id=\"ea0c\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">We need to know that with virtual threads, since they are very cheap, we don\u2019t need to pool them. Also, we need to know that a virtual thread can pull its stack from the underlying platform thread to the heap when blocking for any operations and, when complete, can pin its stack to any of the available platform threads.\u00a0<strong class=\"sa ep\">This is new in Java, and it\u2019s great.<\/strong><\/p>\n<p id=\"689c\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">Consider the following code snippets:<\/p>\n<pre class=\"sw sx sy sz ta tb tc td bo te tf tg\"><span id=\"b436\" class=\"th ti np tc b bf tj tk l tl tm\" data-selectable-paragraph=\"\"> <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> Weather <span class=\"hljs-title\">readFromServerA<\/span>() throws InterruptedException<\/span> {\r\n        Thread.sleep(RandomUtils.nextLong(<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">100<\/span>));\r\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> Weather(<span class=\"hljs-string\">\"Partly Sunny\"<\/span>, <span class=\"hljs-string\">\"server-A\"<\/span>);\r\n    }\r\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> Weather <span class=\"hljs-title\">readFromServerB<\/span>() throws InterruptedException<\/span> {\r\n        Thread.sleep(RandomUtils.nextLong(<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">100<\/span>));\r\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> Weather(<span class=\"hljs-string\">\"Partly Sunny\"<\/span>, <span class=\"hljs-string\">\"server-B\"<\/span>);\r\n    }\r\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> Weather <span class=\"hljs-title\">readFromServerC<\/span>() throws InterruptedException<\/span> {\r\n        Thread.sleep(RandomUtils.nextLong(<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">100<\/span>));\r\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> Weather(<span class=\"hljs-string\">\"Partly Sunny\"<\/span>, <span class=\"hljs-string\">\"server-C\"<\/span>);\r\n    }<\/span><\/pre>\n<p id=\"191c\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">The above code returns weather information. It mimics a server and returns information in 1\u2013100 ms. Our requirement is to query all servers and get the results. The one that sends the result first will be considered, and our logic should be such that we should immediately cancel the other two requests (by interrupting them).<\/p>\n<p id=\"eead\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">Let\u2019s write this logic with the help of the\u00a0<strong class=\"sa ep\">StructuredTaskScope<\/strong>\u00a0object.<\/p>\n<pre class=\"sw sx sy sz ta tb tc td bo te tf tg\"><span id=\"6ee7\" class=\"th ti np tc b bf tj tk l tl tm\" data-selectable-paragraph=\"\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> Weather <span class=\"hljs-title\">readWeather<\/span>() throws ExecutionException, InterruptedException, TimeoutException<\/span> {\r\n        <span class=\"hljs-keyword\">try<\/span> (<span class=\"hljs-keyword\">var<\/span> scope = <span class=\"hljs-keyword\">new<\/span> StructuredTaskScope.ShutdownOnSuccess&lt;Weather&gt;()){\r\n\r\n           <span class=\"hljs-comment\">\/\/ need to call fork and pass runnables or callables<\/span>\r\n           scope.fork(() -&gt; Weather.readWeatherFromServerA());\r\n           scope.fork(() -&gt; Weather.readWeatherFromServerB());\r\n           scope.fork(() -&gt; Weather.readWeatherFromServerC());\r\n\r\n           <span class=\"hljs-comment\">\/\/ now we block, blocking in cheap in virtual threas<\/span>\r\n           <span class=\"hljs-comment\">\/\/ so no issue here<\/span>\r\n           scope.<span class=\"hljs-keyword\">join<\/span>();\r\n\r\n           Weather weather = scope.result();\r\n           <span class=\"hljs-keyword\">return<\/span> weather;\r\n\r\n}\r\n<\/span><\/pre>\n<p id=\"f487\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">The pattern is quite simple; we have used the\u00a0<strong class=\"sa ep\">ShutdownOnSuccess<\/strong>\u00a0variation of\u00a0<strong class=\"sa ep\">StructuredTaskScope<\/strong>, which is provided out of the box by the JVM.<\/p>\n<p id=\"f694\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">We just submit the three tasks one by one to fetch weather from different servers.\u00a0<strong class=\"sa ep\">fork()<\/strong>\u00a0method accepts\u00a0<strong class=\"sa ep\">callables<\/strong>.<\/p>\n<p id=\"4be1\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">Then we block with the help of scope. join(), and why do we block? Why not now? Blocking is cheap, and we should encourage developers to block.<\/p>\n<p id=\"c469\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">Finally, we call the result method explicitly provided by the\u00a0<strong class=\"sa ep\">scope.result()<\/strong>\u00a0method.<\/p>\n<p id=\"3c28\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">Now let\u2019s see which request gets the result and which of the other two is cancelled.<\/p>\n<pre class=\"sw sx sy sz ta tb tc td bo te tf tg\"><span id=\"f145\" class=\"th ti np tc b bf tj tk l tl tm\" data-selectable-paragraph=\"\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> Weather <span class=\"hljs-title.function\">readWeather<\/span><span class=\"hljs-params\">()<\/span> <span class=\"hljs-keyword\">throws<\/span> InterruptedException, ExecutionException {\r\n\r\n        <span class=\"hljs-keyword\">try<\/span>(<span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">scope<\/span> <span class=\"hljs-operator\">=<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-title.class\">StructuredTaskScope<\/span>.ShutdownOnSuccess&lt;Weather&gt;();){\r\n\r\n            scope.fork(() -&gt; Weather.readWeatherFromServerA());\r\n            scope.fork(() -&gt; Weather.readWeatherFromServerB());\r\n            scope.fork(() -&gt; Weather.readWeatherFromServerC());\r\n\r\n\r\n            Future&lt;Weather&gt; futureA = scope.fork(Weather::readWeatherFromServerA);\r\n            Future&lt;Weather&gt; futureB = scope.fork(Weather::readWeatherFromServerB);\r\n            Future&lt;Weather&gt; futureC = scope.fork(Weather::readWeatherFromServerC);\r\n\r\n            scope.join();\r\n\r\n            System.out.println(<span class=\"hljs-string\">\"futureA.state() = \"<\/span> + futureA.state());\r\n            System.out.println(<span class=\"hljs-string\">\"futureB.state() = \"<\/span> + futureB.state());\r\n            System.out.println(<span class=\"hljs-string\">\"futureC.state() = \"<\/span> + futureC.state());\r\n\r\n            <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">weather<\/span> <span class=\"hljs-operator\">=<\/span> scope.result();\r\n\r\n           \r\n\r\n            <span class=\"hljs-keyword\">return<\/span> weather;\r\n        }\r\n\r\n}\r\n\r\n <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title.function\">main<\/span><span class=\"hljs-params\">(String[] args)<\/span> <span class=\"hljs-keyword\">throws<\/span> InterruptedException, ExecutionException {\r\n       <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">weather<\/span> <span class=\"hljs-operator\">=<\/span> Weather.readWeather();\r\n\r\n    }<\/span><\/pre>\n<p id=\"c990\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">Let\u2019s run this programme quite a few times.<\/p>\n<pre class=\"sw sx sy sz ta tb tc td bo te tf tg\"><span id=\"792e\" class=\"th ti np tc b bf tj tk l tl tm\" data-selectable-paragraph=\"\">WARNING: Using incubator modules: jdk.incubator.concurrent\r\nfutureA.<span class=\"hljs-built_in\">state<\/span>() = FAILED\r\nfutureB.<span class=\"hljs-built_in\">state<\/span>() = FAILED\r\nfutureC.<span class=\"hljs-built_in\">state<\/span>() = SUCCESS<\/span><\/pre>\n<pre class=\"lc tb tc td bo te tf tg\"><span id=\"65ef\" class=\"th ti np tc b bf tj tk l tl tm\" data-selectable-paragraph=\"\">futureA.<span class=\"hljs-title.function\">state<\/span>() = <span class=\"hljs-variable.constant\">FAILED<\/span>\r\nfutureB.<span class=\"hljs-title.function\">state<\/span>() = <span class=\"hljs-variable.constant\">SUCCESS<\/span>\r\nfutureC.<span class=\"hljs-title.function\">state<\/span>() = <span class=\"hljs-variable.constant\">FAILED<\/span><\/span><\/pre>\n<p id=\"1601\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">We can see that different serves returned success, while the other two failed. It\u2019s just for explanation; it\u2019s technical code; we just need to query different servers as below.<\/p>\n<pre class=\"sw sx sy sz ta tb tc td bo te tf tg\"><span id=\"d75d\" class=\"th ti np tc b bf tj tk l tl tm\" data-selectable-paragraph=\"\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> Weather <span class=\"hljs-title\">readWeather<\/span>() throws ExecutionException, InterruptedException, TimeoutException<\/span> {\r\n        <span class=\"hljs-keyword\">try<\/span> (<span class=\"hljs-keyword\">var<\/span> scope = <span class=\"hljs-keyword\">new<\/span> StructuredTaskScope.ShutdownOnSuccess&lt;Weather&gt;()){\r\n\r\n           <span class=\"hljs-comment\">\/\/ need to call fork and pass callables<\/span>\r\n           scope.fork(() -&gt; Weather.readWeatherFromServerA());\r\n           scope.fork(() -&gt; Weather.readWeatherFromServerB());\r\n           scope.fork(() -&gt; Weather.readWeatherFromServerC());\r\n\r\n           <span class=\"hljs-comment\">\/\/ now we block, blocking is cheap in virtual threads<\/span>\r\n           <span class=\"hljs-comment\">\/\/ so no issue here<\/span>\r\n           scope.<span class=\"hljs-keyword\">join<\/span>();\r\n\r\n           Weather weather = scope.result();\r\n           <span class=\"hljs-keyword\">return<\/span> weather;\r\n\r\n}<\/span><\/pre>\n<p id=\"ba25\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">And by the way, the above code can be written even more clearly with the help of method references.<\/p>\n<pre class=\"sw sx sy sz ta tb tc td bo te tf tg\"><span id=\"ef9a\" class=\"th ti np tc b bf tj tk l tl tm\" data-selectable-paragraph=\"\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-built_in\">static<\/span> Weather <span class=\"hljs-title.function.invoke\">readWeather<\/span>() throws InterruptedException, ExecutionException {\r\n        <span class=\"hljs-keyword\">try<\/span>(<span class=\"hljs-keyword\">var<\/span> scope = <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-title.class\">StructuredTaskScope<\/span>.ShutdownOnSuccess&lt;Weather&gt;();){\r\n            scope.<span class=\"hljs-title.function.invoke\">fork<\/span>(<span class=\"hljs-title.class\">Weather<\/span>::<span class=\"hljs-variable.constant\">readWeatherFromServerA<\/span>);\r\n            scope.<span class=\"hljs-title.function.invoke\">fork<\/span>(<span class=\"hljs-title.class\">Weather<\/span>::<span class=\"hljs-variable.constant\">readWeatherFromServerB<\/span>);\r\n            scope.<span class=\"hljs-title.function.invoke\">fork<\/span>(<span class=\"hljs-title.class\">Weather<\/span>::<span class=\"hljs-variable.constant\">readWeatherFromServerC<\/span>);\r\n            scope.<span class=\"hljs-title.function.invoke\">join<\/span>(); \r\n            <span class=\"hljs-keyword\">var<\/span> weather = scope.<span class=\"hljs-title.function.invoke\">result<\/span>();\r\n            <span class=\"hljs-keyword\">return<\/span> weather;\r\n        }\r\n    }<\/span><\/pre>\n<p id=\"694d\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\"><strong class=\"sa ep\">How is it different from the ExecutorService?<\/strong><\/p>\n<p id=\"9b06\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">The ExecutorService lifecycle runs along with the lifecycle of the application. Once the application is started, the executor service framework starts and shuts down once the application is terminated.<\/p>\n<p id=\"ce03\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">But here, for every request call, StructuredTaskScope is created and destroyed as soon as we exit the method. We can do it because virtual threads are cheap \u2014 around 1000 times cheaper than platform threads.<\/p>\n<p id=\"7145\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">Secondly, the above code can be written with the help of complete stage APIs, but with those frameworks, we need to welcome callback hells and long nesting of codes. Who likes callback hell? Nobody.<\/p>\n<p id=\"90f2\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">We will look into more such patterns. It\u2019s a completely new concurrency model, and we need to see exactly what patterns are going to become the norm, but I think that this is the pattern that we are going to use.<\/p>\n<p id=\"d2e0\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">One reminder: it will work on Java 19 and above with the preview feature enabled.<\/p>\n<p id=\"286c\" class=\"pw-post-body-paragraph ry rz np sa b sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv nj bj\" data-selectable-paragraph=\"\">This is just the start, in coming blogs, we will see some variations of <strong class=\"sa ep\">StructuredTaskScope<\/strong>. Stay tuned.<\/p>\n<p data-selectable-paragraph=\"\">The original blog is <a href=\"https:\/\/medium.com\/@anil.java.story\/structured-task-scope-new-concurrency-model-c5fa38613f47\">here<\/a>.<\/p>\n<p data-selectable-paragraph=\"\">Explore our\u00a0<a href=\"https:\/\/www.tothenew.com\/blog\/\">blogs<\/a> for an in-depth understanding of various trending topics.<\/p>\n<div class=\"ap-custom-wrapper\"><\/div><!--ap-custom-wrapper-->","protected":false},"excerpt":{"rendered":"<p>Since the advent of virtual threads in Java, we have had a new and nice concurrency model introduced as\u00a0StructuredTaskScope. We will see some patterns in this new model. We need to know that with virtual threads, since they are very cheap, we don\u2019t need to pool them. Also, we need to know that a virtual [&hellip;]<\/p>\n","protected":false},"author":1520,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":5},"categories":[446],"tags":[5134,5225,5224],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/57370"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/1520"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=57370"}],"version-history":[{"count":3,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/57370\/revisions"}],"predecessor-version":[{"id":58364,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/57370\/revisions\/58364"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=57370"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=57370"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=57370"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}