{"id":15069355,"url":"https://github.com/alexcheng1982/jdk-loom-faq","last_synced_at":"2026-03-03T15:02:31.492Z","repository":{"id":49279885,"uuid":"497505734","full_name":"alexcheng1982/jdk-loom-faq","owner":"alexcheng1982","description":"JDK Project Loom FAQ and Example Code","archived":false,"fork":false,"pushed_at":"2023-12-28T01:48:25.000Z","size":412,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-24T14:50:35.516Z","etag":null,"topics":["java","jdk","loom"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alexcheng1982.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-29T06:09:37.000Z","updated_at":"2025-02-17T07:57:24.000Z","dependencies_parsed_at":"2025-02-17T16:46:37.280Z","dependency_job_id":null,"html_url":"https://github.com/alexcheng1982/jdk-loom-faq","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexcheng1982%2Fjdk-loom-faq","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexcheng1982%2Fjdk-loom-faq/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexcheng1982%2Fjdk-loom-faq/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexcheng1982%2Fjdk-loom-faq/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexcheng1982","download_url":"https://codeload.github.com/alexcheng1982/jdk-loom-faq/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248261302,"owners_count":21074220,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","jdk","loom"],"created_at":"2024-09-25T01:41:59.459Z","updated_at":"2025-10-06T14:46:34.227Z","avatar_url":"https://github.com/alexcheng1982.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JDK Project Loom FAQ and Example Code\n\nJDK Project Loom FAQ and example code\n\n## General\n\n### What's Project Loom?\n\n\u003e **Project Loom** is intended to explore, incubate and deliver Java VM features and APIs built on top of them for the\n\u003e purpose of supporting easy-to-use, high-throughput lightweight concurrency and new programming models on the Java\n\u003e platform.\n\u003e\n\u003e [Project Loom Wiki](https://wiki.openjdk.java.net/display/loom/Main)\n\n### How can I use Project Loom?\n\nAccording to the JDK release process, features in Project Loom will be broken down into several JEPs and made available\nin different JDK releases. For JDK 19, the early-access builds can be downloaded from [jdk.java.net](https://jdk.java.net/19/). JDK 19 builds only contain features targeted to JDK 19.\n\nThe table below shows a list of targeted features in Project Loom.\n\n| Feature                                                     | Target JDK Release | Status                                        |\n| ----------------------------------------------------------- | ------------------ | --------------------------------------------- |\n| [Virtual Threads](https://openjdk.java.net/jeps/425)        | 19                 | Preview                                       |\n| [Structured Concurrency](https://openjdk.java.net/jeps/428) | 19                 | [Incubator](https://openjdk.java.net/jeps/11) |\n\nIn the meantime, you can download Project Loom early-access builds from [Loom website](https://jdk.java.net/loom/).\n\nFeatures in Project Loom are either in preview or incubating status. To enable preview features, the `--enable-preview`\noption needs to be passed to `javac` or `java` command. For incubating features, the corresponding JDK modules need to\nbe added explicitly. For example, using the option `--add-modules jdk.incubator.concurrent` to enabled the module for\nstructured concurrency.\n\n## Virtual Thread\n\n### What's virtual thread?\n\nBefore Project Loom, there is only one type of threads in Java, which is called *platform thread* in Project Loom.\nPlatform threads are typically mapped 1:1 to kernel threads scheduled by the operating system. In Project Loom, virtual\nthreads are introduced as a new type of threads.\n\nVirtual threads are typically *user-mode threads* scheduled by the Java runtime rather than the operating system.\nVirtual threads are mapped M:N to kernel threads.\n\nPlatform and virtual threads are both represented using `java.lang.Thread`.\n\n### Why we need virtual threads?\n\nThe main motivation of using virtual threads is to provide a scalable way to implement *thread-per-request* style\nrequest handling. When writing server applications, it's natural to dedicate one thread to a request to handle it for\nits entire duration. This is because requests are independent of each other. This *thread-per-request* style is easy to\nunderstand and program, and also very easy to debug and profile.\n\nHowever, this `thread-per-request` style cannot be simply implemented using platform threads. Platform threads are\nimplemented as wrappers around the operating system threads. OS threads are costly, and the number of available threads\nis limited. For a server that handles a very large number of requests concurrently, it's not feasible to create a thread\nfor each request.\n\n### How to create virtual threads?\n\nThe first approach to create virtual threads is using the `Thread.ofVirtual` method.\n\nIn the code below, a new virtual thread is created and started. The return value is an instance of `java.lang.Thread`\nobject.\n\n```java\nvar thread = Thread.ofVirtual().name(\"my virtual thread\")\n    .start(() -\u003e System.out.println(\"I'm running\"))\n```\n\nThe second approach is using `Thread.startVirtualThread(Runnable task)` method. This is the same as\ncalling `Thread.ofVirtual().start(task)`.\n\nThe third approach is using `ThreadFactory`.\n\n```java\nvar factory = Thread.ofVirtual().factory();\nvar thread = factory.newThread(() -\u003e System.out.println(\"Create in factory\"));\n```\n\n### How to check if a thread is virtual?\n\nThe new `isVirtual()` method in `java.lang.Thread` returns `true` is this thread is a virtual thread.\n\n### Does a virtual thread has name?\n\nA virtual thread doesn't have a name by default. The `getName()` method returns the empty string if a thread name is not\nset.\n\nThe thread name can be set using the `setName()` method, or using the `name` method of `Thread.Builder` returned\nfrom `Thread.ofVirtual()`.\n\nIt's recommended to always set a name for debugging and error diagnosis purpose.\n\n### Can virtual threads be non-daemon threads?\n\nNo. Virtual threads are always daemon threads. So they cannot prevent JVM from terminating. Calling `setDaemon(false)`\non a virtual thread will throw an `IllegalArgumentException` exception.\n\n### Can the priority of a virtual thread be changed?\n\nNo. Virtual threads have a fixed priority of `Thread.NORM_PRIORITY`. The `Thread.setPriority(int)` method has no effect\non virtual threads.\n\n### Can virtual threads support thread-local variables?\n\nYes. Virtual threads support both thread-local variables (`ThreadLocal`) and inheritable thread-local\nvariables (`InheritableThreadLocal`).\n\n### Can thread local variables be disabled for virtual threads?\n\nYes. If no thread-local variables are required, the support can be disabled using methods in `Thread.Builder`.\n\nTo disable thread-local variables, we can use the `allowSetThreadLocals(boolean allow)` method.\n\nWhen thread-local variables are not allowed:\n\n* Using the `ThreadLocal.set(Object)` method to set a value for a thread-local variable will\n  throw `UnsupportedOperationException`.\n* The`ThreadLocal.get()` method always returns the initial value.\n\nIn the code below, calling `threadLocal.set(100)` throws `UnsupportedOperationException`.\n\n```java\nThreadLocal\u003cInteger\u003e threadLocal = new ThreadLocal\u003c\u003e();\nThread.ofVirtual()\n  .allowSetThreadLocals(false)\n  .start(() -\u003e threadLocal.set(100)) // throws UnsupportedOperationException\n  .join();\n```\n\nIn the code below, the initial value `1` of the thread-local variable is printed out.\n\n```java\nThreadLocal\u003cInteger\u003e threadLocal = ThreadLocal.withInitial(() -\u003e 1);\nThread.ofVirtual()\n  .allowSetThreadLocals(false)\n  .start(() -\u003e System.out.println(threadLocal.get())) // The output is \"1\"\n  .join();\n```\n\nTo not inherit the values of inheritable thread-local variables, you can use\nthe `inheritInheritableThreadLocals(boolean inherit)` method.\n\nIn the code below, the `InheritableThreadLocal` object has its value set to `300` in the parent thread. However, the\nchild thread disabled inheritance of inheritable thread-local variables, the `InheritableThreadLocal`object has the\nvalue `null` in the child thread.\n\n```java\nvar inheritableThreadLocal = new InheritableThreadLocal\u003cInteger\u003e();\nThread.ofVirtual()\n  .name(\"parent\")\n  .start(() -\u003e {\n    inheritableThreadLocal.set(300);\n    Thread.ofVirtual()\n      .name(\"child\")\n       .inheritInheritableThreadLocals(false)\n        .start(() -\u003e System.out.println(inheritableThreadLocal.get())); // The output is \"null\"\n  }).join();\n```\n\n### Should virtual threads be pooled?\n\nNo. Virtual threads are light-weight. There is no need to pool them.\n\nSometimes a thread pool is used to limit concurrent access to a limited resource. For example, if the upstream server\ncan only handle a limit to 10 concurrent requests, a thread pool with maximum 10 threads may be used to enforce the\nlimitation. However, this pattern shouldn't be used for virtual threads. Structs like `Semaphore` should be used to\nguard access to a limited resource.\n\n### How are virtual threads scheduled?\n\nVirtual threads are scheduled by the JDK. JDK assigns virtual threads to platform threads, then those platform threads\nare scheduled by the operating system.\n\nThe platform thread which a virtual thread is assigned to is called the virtual thread's `carrier`. A virtual thread may\nbe scheduled to multiple carriers during its lifetime. The identity of the carrier is unavailable to the virtual thread.\n\nJDK scheduler for virtual threads is a work-stealing `ForkJoinPool` working in FIFO mode. The number of platform threads used for scheduling is determined by the `parallelism`  of this `ForkJoinPool`. The default thread number is the same as CPU processors, which is retrieved by calling `Runtime.availableProcessors()`. The thread number can also be set via system property `jdk.virtualThreadScheduler.parallelism`.\n\n### How are virtual threads executed?\n\nWhen executing code in virtual threads, JDK scheduler assigns the virtual thread to a platform thread. This is called mounting a virtual thread on a platform thread. The selected platform thread becomes the carrier of this virtual thread. After executing some code, the virtual thread may unmount from the platform thread.\n\nWhen the virtual thread is blocking on I/O or other blocking operations, it can be unmounted from the platform thread. When the blocking operation is finished, the virtual thread can be mounted to another platform thread for execution. Mounting and unmounting of virtual threads are transparent to code executed in virtual threads.\n\nSome blocking operations in the JDK do not unmount the virtual thread, and thus block both its carrier and the underlying OS thread. This is because of limitations either at the OS level or at the JDK level. To compensate  for the capture of the OS thread, the parallelism of the scheduler will be temporarily expanded. This means that the number of platform threads in the scheduler's `ForkJoinPool` may temporarily exceed the configured value. The maximum number of platform threads available to the scheduler can be configured with the system property `jdk.virtualThreadScheduler.maxPoolSize`. \n\nThere are two scenarios in which a virtual thread will be pinned to its carrier and cannot be unmounted during blocking operations:\n\n* When it executes code inside a `synchronized` method or block,\n* When it executes a `native` method or a foreign function.\n\nPinning may hinder an application's scalability. It a virtual thread performs a blocking operation while it's pinned, then  its carrier and the underlying OS thread are blocked for the duration of the operation. The scheduler doesn't compensate for pinning by expanding its parallelism. To avoid frequent and long-lived pinning, `synchronized` blocks or methods that run frequently and guard long I/O operations should be replaced with `java.util.concurrent.locks.ReentrantLock`.\n\n\n## `ExecutorService`\n\n### Can `ExecutorService` use virtual threads?\n\nAn `ExecutorService` can start a virtual thread for each task. This kind of `ExecutorService`s can be created\nusing `Executors.newVirtualThreadPerTaskExecutor()` or `Executors.newThreadPerTaskExecutor(ThreadFactory threadFactory)`\nmethods. The number of virtual threads created by the `Executor` is unbounded.\n\nIn the code below, a new `ExecutorService` is created to use virtual threads. 10000 tasks are submitted to\nthis `ExecutorService`.\n\n```java\ntry (var executor = Executors.newVirtualThreadPerTaskExecutor()) {\n  IntStream.range(0, 10_000).forEach(i -\u003e executor.submit(() -\u003e {\n    Thread.sleep(Duration.ofSeconds(1));\n    return i;\n  }));\n}\n```\n\n## `Future`\n\n### What are changes to `Future` in Loom?\n\nA new enum `Future.State` is added to represent the state of a `Future`.\n\n| Enum value | Description |\n| ------ | ---- |\n| `CANCELLED` |   The task was cancelled.   |\n|  `FAILED`      |   The task completed with an exception.   |\n|   `RUNNING`     |  The task has not completed.    |\n|   `SUCCESS`     |   The task completed with a result.   |\n\nThe `state()` method of `Future` can retrieve the state of a `Future`.\n\nThe methods `resultNow()` and `exceptionNow()` can get the result or exception of a  `Future` without waiting, respectively.\n\n## Debugging\n\n### How to debug virtual threads?\n\nVirtual threads are instances of `java.lang.Thread`. Existing tools to debug, profile, and monitor threads can still work with virtual threads. Java debuggers can step through virtual threads, show call stacks, and inspect variables in stack frames.\n\nThe screen-shot below shows debugging a virtual thread in IntelliJ IDEA.\n\n![](./assets/debug-virtual-thread.png)\n\n### How to view thread dump of a virtual thread?\n\nThe thread dump plays an important role in troubleshooting applications. JDK's traditional thread dump presents a flat list of threads. This is unsuitable for thousands or millions of virtual threads. The traditional thread dump format is extended to include virtual threads. A new kind of thread dump is introduced to present virtual threads.\n\nTo get a thread dump, the process id should be obtained first. This can be done using the `jps` command.\n\nAfter obtaining the process id, use `jcmd` command to get a thread dump. To visualize and analyze a great number of virtual threads, `jcmd` can emit the new thread dump in JSON format.\n\n```sh\n$ jcmd \u003cpid\u003e Thread.dump_to_file -format=json \u003cfile\u003e\n```\n\nSee [here](./assets/thread-dump.json) for an example of thread dump JSON file.\n\n### How to use JFR to view virtual threads events?\n\nJDK Flight Recorder (JRF) adds events related to virtual threads.\n\n\n| Event                           | Description                                    | Enabled by default |\n| ------------------------------- | ---------------------------------------------- | ------------------ |\n| `jdk.VirtualThreadStart`        | A virtual thread starts.                       | No                 |\n| `jdk.VirtualThreadEnd`          | A virtual thread ends.                         | No                 |\n| `jdk.VirtualThreadPinned`       | A virtual thread was parked while pinning.     | Yes                |\n| `jdk.VirtualThreadSubmitFailed` | Starting or unparking a virtual thread failed. | Yes                |\n\nThe screen-shot below shows virtual threads events in JFR.\n\n![JFR events](./assets/jfr-events.png)\n\nEvents for virtual threads starting and ending need to be enabled explicitly.\n\n![JFR enable events](./assets/jfr-enable-events.png)\n\n## Structured Concurrency\n\n### What's structured concurrency?\n\nStructured concurrency is a coined [term](https://250bpm.com/blog:71/). It's described in details in this [post](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/). The key point of structured concurrency is `structured`. Structured means the structure of concurrent tasks should match the code structure. By leveraging structured concurrency, developers can write concurrent programs just like single-threaded programs. All the heavy-lifting jobs are done by the underlying framework.\n\n\u003e [Kotin Coroutines](https://kotlinlang.org/docs/coroutines-overview.html) is a greate example of using [structured concurrency](https://kotlinlang.org/docs/coroutines-basics.html#structured-concurrency). New coroutines can be only launched in a specific `CoroutineScope` which delimits the lifetime of the coroutine. \n\nConsidering that we are creating an API to expose a user's information in an e-commerce application. When using microservice architecture, a user's information may be maintained by different services. We may need to fetch data from multiple sources and assemble them to get the final result.\n\nThe code below shows a simple method `getUser()` to load and assemble a user's information. It fetches data from three different sources. We can treat the `getUser()` method as a task, while those three methods `fetchUserBasicInfo`, `fetchFavoriteStores` and `fetchOrders` are subtasks of the main task.\n\n```java\npublic UserData getUser(String userId) {\n    UserBasicInfo basicInfo = fetchUserBasicInfo(userId);\n    List\u003cStore\u003e favoriteStores = fetchFavoriteStores(userId);\n    List\u003cOrder\u003e orders = fetchOrders(userId);\n    return assemble(basicInfo, favoriteStores, orders);\n}\n```\n\nApparently, there is a dependency between the main task and subtasks.\n\n* The main task can only complete when all the subtasks complete.\n* If any of the subtask failed, the main task will also fail. All other ongoing subtasks should be cancelled, because there results won't be used.\n\nIf all subtasks are executed synchronously in a single thread, then we can easily get the following assumptions:\n\n* If `fetchUserBasicInfo` fails, both `fetchFavoriteStores` and `fetchOrders` won't be executed, `getUser` returns immediately.\n* `getUser` completes after `assemble` completes, `fetchUserBasicInfo`, `fetchFavoriteStores` and `fetchOrders` all completes successfully before that.\n\nThese assumptions can be easily derived from the code structure of `getUser` method. Those three methods are contained in the block of `getUser` method, so their life time is confined by the outer method.\n\nIf subtasks are executed asynchronously in different threads, then we cannot make the same assumption.\n\n* Even `getUser` returns, all the subtasks may still be running in their own threads.\n* If `fetchUserBasicInfo` fails, other two subtasks may still run to their ends.\n\nIt's possible to implement `getUser` correctly with multithreading support in Java, including `Executor`s and thread pools. However, it's not an easy task, even for most-experienced developers. You have to deal with `Executors`, thread pools, thread interruption, timeout, cooperative cancellation,  graceful shutdown. \n\nWith structured concurrency, we can simply treat concurrent subtasks as they are running in a single thread. Those assumptions still hold. This makes concurrent programming much easier.\n\n### How to use structured concurrency?\n\nThe main API to use structured concurrency is `jdk.incubator.concurrent.StructuredTaskScope`. A `StructuredTaskScope` object is a scope where subtasks are executed in.  The workflow of using `StructuredTaskScope` is as follows:\n\n1. The main task creates a `StructuredTaskScope` object.\n2. Use the `fork()` method to create a new subtask.\n3. Calls the `join()` or `joinUntil()` method to wait for subtasks to complete or be cancelled.\n4. After joining, handle any errors in the subtasks and process their results.\n5. Close the scope, usually implicitly via `try`-with-resources. \n6. During the tasks execution, calling the `shutdown()` method to request cancellation of all remaining subtasks.\n\nThe thread that creates a `StructuredTaskScope` object is the *owner* of the scope.\n\nThe table below shows methods of `StructuredTaskScope`.\n\n| Method                                                     | Description                                    |\n| ---------------------------------------------------------- | ---------------------------------------- |\n| `\u003cU extends T\u003e Future\u003cU\u003e fork(Callable\u003c? extends U\u003e task)` | Starts a new thread to run the given task.                   |\n| `StructuredTaskScope\u003cT\u003e join()`                            | Wait for all threads to finish or the task scope to shut down.  |\n| `StructuredTaskScope\u003cT\u003e joinUntil(Instant deadline)`       | Wait for all threads to finish or the task scope to shut down, up to the given deadline.    |\n| `shutdown()`                                               | Shut down the task scope without closing it.                            |\n| `close()`                                                  | Closes this task scope.                          |\n\nThere are two types of `StructuredTaskScope` subclasses that are commonly used, `StructuredTaskScope.ShutdownOnFailure`  and `StructuredTaskScope.ShutdownOnSuccess`.\n\n* `ShutdownOnFailure` captures the exception of the first subtask to complete abnormally. The policy implemented by this class is intended for cases where the results for all subtasks are required (\"invoke all\"); if any subtask fails then the results of other unfinished subtasks are no longer needed.\n\n* `ShutdownOnSuccess` captures the result of the first subtask to complete successfully. The policy implemented by this class is intended for cases where the result of any subtask will do (\"invoke any\") and where the results of other unfinished subtask are no longer needed.\n\nThe following code shows an example of using `StructuredTaskScope.ShutdownOnFailure`. The main task is `calculate` method. It creates a `ShutdownOnFailure` object, then it forks a subtask that calls the `op1` method. The `calculateInner` method creates another `ShutdownOnFailure` object which forks two subtasks to call the `op21` and `op22` methods.  The `join` method waits for the subtasks to complete. The `throwIfFailed` method throws if a task completes abnormally. The `fork` method returns a `Future` object. After `join` method returns, we can be sure that the `Future` object completes, so it's safe to call `resultNow` to get the actual value.\n\n```java\npublic class StructuredCurrencyExample {\n\n  public static void main(String[] args) throws Exception {\n    System.out.println(\n        Helper.timed(() -\u003e new StructuredCurrencyExample().calculate()));\n  }\n\n  public int calculate() throws InterruptedException, ExecutionException {\n    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {\n      Future\u003cInteger\u003e v1 = scope.fork(this::op1);\n      int v2 = calculateInner();\n      scope.join();\n      scope.throwIfFailed();\n      return v1.resultNow() * v2;\n    }\n  }\n\n  private int calculateInner() throws InterruptedException, ExecutionException {\n    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {\n      Future\u003cInteger\u003e v21 = scope.fork(this::op21);\n      Future\u003cInteger\u003e v22 = scope.fork(this::op22);\n      scope.join();\n      scope.throwIfFailed();\n      return v21.resultNow() + v22.resultNow();\n    }\n  }\n\n  private int op1() {\n    System.out.println(\"Operation 1 starts\");\n    try {\n      Thread.sleep(Duration.ofSeconds(3));\n    } catch (InterruptedException e) {\n      // ignored\n    }\n    System.out.println(\"Operation 1 finishes\");\n    return 1;\n  }\n\n  private int op21() {\n    System.out.println(\"Operation 2.1 starts\");\n    try {\n      Thread.sleep(Duration.ofSeconds(4));\n    } catch (InterruptedException e) {\n      // ignored\n    }\n    System.out.println(\"Operation 2.1 finishes\");\n    return 3;\n  }\n\n  private int op22() {\n    System.out.println(\"Operation 2.2 starts\");\n    try {\n      Thread.sleep(Duration.ofSeconds(2));\n    } catch (InterruptedException e) {\n      // ignored\n    }\n    System.out.println(\"Operation 2.2 finishes\");\n    return 3;\n  }\n}\n```\n\nThe code above demonstrates a task tree. The chart below shows the task tree. The parent task supervises subtasks in the children nodes.\n\n```\n       calculate\n     /           \\\n    op1      calculateInner\n            /           \\\n          op21          op22\n```\n\n### How to perform *invokeAll* actions using structured concurrency?\n\n `StructuredTaskScope.ShutdownOnFailure` can be used to perform *invokeAll* actions.\n\nThe code below shows an example of using `ShutdownOnFailure`. In the `invokeAll` method, `10000` tasks are created to return an integer, then all these integers are summed  up.\n\n```java\npublic class InvokeAll {\n\n  public static void main(String[] args) throws Exception {\n    System.out.println(Helper.timed(() -\u003e new InvokeAll().invokeAll()));\n  }\n\n  public long invokeAll() throws InterruptedException, ExecutionException {\n    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {\n      var futures = subTasks().map(scope::fork).toList();\n      scope.join();\n      scope.throwIfFailed();\n      return futures.stream().map(Future::resultNow).reduce(0, Integer::sum);\n    }\n  }\n\n  private Stream\u003cCallable\u003cInteger\u003e\u003e subTasks() {\n    return IntStream.range(0, 10_000)\n        .mapToObj(\n            i -\u003e\n                () -\u003e {\n                  try {\n                    Thread.sleep(Duration.ofSeconds(ThreadLocalRandom.current().nextLong(3)));\n                  } catch (InterruptedException e) {\n                    // ignore\n                  }\n                  return i;\n                });\n  }\n}\n```\n\n### How to perform *invokeAny* actions using structured concurrency?\n\n`StructuredTaskScope.ShutdownOnSuccess` can be used to perform *invokeAny* actions.\n\nThe code below shows an example of using `ShutdownOnSuccess`. This example is similar with the *InvokeAll* code. It starts `1000` tasks and returns the number of tasks that actually succeeded. When running the program, we can see that the number of succeeded tasks varies. Usually there will be hundreds of completed tasks. This is because it takes time to cancel tasks.\n\n```java\npublic class InvokeAny {\n\n  public static void main(String[] args) throws Exception {\n    System.out.println(Helper.timed(() -\u003e new InvokeAny().invokeAny()));\n  }\n\n  public long invokeAny() throws InterruptedException {\n    try (var scope = new StructuredTaskScope.ShutdownOnSuccess\u003c\u003e()) {\n      var futures = subTasks().map(scope::fork).toList();\n      scope.join();\n      return futures.stream().filter(f -\u003e !f.isCancelled()).count();\n    }\n  }\n\n  private Stream\u003cCallable\u003cInteger\u003e\u003e subTasks() {\n    return IntStream.range(0, 1000)\n        .mapToObj(\n            i -\u003e\n                () -\u003e {\n                  try {\n                    Thread.sleep(Duration.ofSeconds(1 + ThreadLocalRandom.current().nextLong(5)));\n                  } catch (InterruptedException e) {\n                    // ignore\n                  }\n                  return i;\n                });\n  }\n}\n```\n\n### What's the difference between `shutdown` and `close` of `StructuredTaskScope`?\n\nThe `shutdown` method of `StructuredTaskScope` will:\n\n* Prevent new threads from starting.\n* Cancel tasks that have threads waiting on a result so that the waiting threads wakeup.\n* Interrupts all unfinished threads in the scope.\n* Wakes up the owner if it is waiting in `join` or `joinUntil`. If the owner is not waiting then its next call to `join` or `joinUntil` will return immediately.\n\nWhen `shutdown` completes,  the `Future` objects for all tasks will be done, normally or abnormally.\n\nThe `shutdown` method may only be invoked by the task scope owner or threads contained in the task scope.\n\nThe `close` method calls the `shutdown` method to shut down the scope first. It then waits for the threads executing any unfinished tasks to finish.  The `close` method is usually called implicitly using `try-with-resources`.\n\nThe `close` method may only be invoked by the task scope owner.\n\n## JDK Libraries\n\n### How virtual threads are used in JDK libraries?\n\nJDK libraries have been upgraded to use virtual threads, especially server-side components.\n\nThe code below shows a simple HTTP server using `com.sun.net.httpserver.HttpServer`. It uses virtual threads to handle requests. `Executors.newThreadPerTaskExecutor(Thread.ofVirtual().name(\"time-server-\", 1).factory())` creates an `Executor` that creates virtual threads for each task.\n\n```java\nimport com.sun.net.httpserver.HttpExchange;\nimport com.sun.net.httpserver.HttpHandler;\nimport com.sun.net.httpserver.HttpServer;\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.concurrent.Executors;\n\npublic class SimpleHttpServer {\n\n  public static void main(String[] args) throws IOException {\n    new SimpleHttpServer().start();\n  }\n\n  public void start() throws IOException {\n    var server = HttpServer.create(new InetSocketAddress(8000), 0);\n    server.createContext(\"/time\", new TimeHandler());\n    server.setExecutor(\n        Executors.newThreadPerTaskExecutor(Thread.ofVirtual().name(\"time-server-\", 1).factory()));\n    server.start();\n    System.out.println(\"Time server started\");\n  }\n\n  private static class TimeHandler implements HttpHandler {\n\n    @Override\n    public void handle(HttpExchange exchange) throws IOException {\n      var response =\n          String.format(\n              \"%s, reported on %s\",\n              LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),\n              Thread.currentThread().getName());\n      exchange.sendResponseHeaders(200, response.length());\n      try (var out = exchange.getResponseBody()) {\n        out.write(response.getBytes());\n      }\n    }\n  }\n}\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexcheng1982%2Fjdk-loom-faq","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexcheng1982%2Fjdk-loom-faq","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexcheng1982%2Fjdk-loom-faq/lists"}