Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Loom is Blooming

Loom is Blooming

You can find the code of the demo here: https://github.com/JosePaumard/Loom_demo

The Loom project has been under work for several years now. Preview versions are available, that give an idea of what it will bring to the Java platform. We can see what patterns we will be able to use, and the performances it will bring. Our good old Threads, created more than 25 years ago, will see a new kind of lightweight threads. Creating a thread will be easier and much cheaper, allowing the creation of millions of them in a single JVM. These new virtual threads bring with them new notions, that will be covered in this talk. You will see structured concurrency, reactive and asynchronous programming, how you can create actors on this new concurrent programming model. You will learn about ScopeLocal, a new way of dealing with ThreadLocal variables, about StructuredExecutor, and about synchronization. A lot of code is shown in this presentation, you will see how Loom can change the way you write your code, the performance gains you may expect, both in your application and for the Java Virtual Machine itself.

F784a8de872a5d98f67ac5ccbd6683d0?s=128

José

May 14, 2022
Tweet

More Decks by José

Other Decks in Programming

Transcript

  1. Loom is Blooming José Paumard Java Developer Advocate Java Platform

    Group Rémi Forax Maître de Conférences Université Gustave Eiffel
  2. https://twitter.com/JosePaumard https://github.com/JosePaumard https://www.youtube.com/user/java https://www.youtube.com/user/JPaumard https://www.youtube.com/c/coursenlignejava https://www.youtube.com/hashtag/jepcafe https://fr.slideshare.net/jpaumard https://www.pluralsight.com/authors/jose-paumard

  3. https://twitter.com/Nope! https://github.com/forax https://speakerdeck.com/forax OpenJDK, ASM, Tatoo, Pro, etc… One of

    the Father of invokedynamic (Java 7) Lambda (Java 8), Module (Java 9) Constant dynamic (Java 11) Record (Java 14 / 15) Valhalla (Java 19+)
  4. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 4

    https://dev.java/
  5. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 5

    https://dev.java/community/
  6. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 6

    https://inside.java/
  7. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 7

    https://jalba.scot 06/22 – 06/26
  8. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 8

    Don’t believe what we say! Loom is a Work in Progress
  9. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 9

    Don’t believe what we say! Loom is a Work in Progress
  10. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 10

    Don’t believe what we say! Loom is a Work in Progress http://jdk.java.net/loom/
  11. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential:

    Internal/Restricted/Highly Restricted 11 It all Started with a Runnable…
  12. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 12

    1995: Thread, Runnable 1995: Threads and Runnables Runnable task = new Runnable() { void run() { System.out.println("I am running in thread " + Thread.currentThread().getName()); } }; Thread thread = new Thread(task); thread.start(); thread.join(); // blocks
  13. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 13

    1995: Thread, Runnable 1995: Threads and Runnables Object key = new Object(); synchronized(key) { System.out.println("Only one thread can execute me!"); }
  14. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 14

    2004: Java 5, java.util.concurrent 2004: Java Util Concurrent Callable<String> task = new Callable<String>() { @Override public String call() throws Exception { return "I am running in thread " + Thread.currentThread().getName(); } };
  15. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 15

    2004: Java 5, java.util.concurrent Wait lists inside! 2004: Java Util Concurrent ExecutorService service = Executors.newFixedThreadPool(4); Future<String> future = service.submit(task);
  16. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 16

    2004: Java 5, java.util.concurrent 2004: Java Util Concurrent String result = future.get(); // blocks String result = future.get(10, TimeUnit.MICROSECONDS); boolean cancelled = future.cancel(true);
  17. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 17

    2004: Java 5, java.util.concurrent 2004: Java Util Concurrent Lock lock = new ReentrantLock(); lock.lock(); try { System.out.println("Only one thread can execute me!"); } finally { lock.unlock(); }
  18. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 18

    2004: Java 5, java.util.concurrent Plus many more concurrent classes: - Lock, Semaphore, Barrier, CountDownLatch - BlockingQueue, ConcurrentMap - CopyOnWriteArrayList 2004: Java Util Concurrent
  19. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 19

    2011 – 2014 (Java 7, Java 8): - Fork / Join, parallel Stream Allows to compute elements in parallel Two phases: - fork = splits a task in two sub-tasks - join = merge the result of two sub-tasks Uses work stealing to spread the tasks among threads 2011: Fork / Join
  20. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 20

    2011 – 2014 (Java 7, Java 8): - CompletionStage, CompletableFuture Subtype of Future Asynchronous programming model Allows to trigger tasks on the outcome of other tasks User can control which thread executes what task Exceptions handling 2014: CompletionStage
  21. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 21

    Once a thread begins to process a task it cannot release it Either the task completes with a result Or is completes with an exception It may be an InterruptedException One thing stays the same
  22. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 22

    2022+ (prev. in Java 19) 2023?: Loom!
  23. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential:

    Internal/Restricted/Highly Restricted 23 Why Do We Need Concurrency?
  24. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 24

    Concurrency may be used in two different contexts: 1) Processing in-memory data in parallel, using all the CPU cores - Each thread uses 100% of your CPU cores - Threads are mostly not blocking Concurrency: Computations vs. I/O
  25. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 25

    Concurrency may be used in two different contexts: 2) Handling numerous blocking requests / responses HTTP Server  1 request <=|=> 1 thread DB Server  1 transaction <=|=> 1 thread Concurrency: Computations vs. I/O
  26. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 26

    Processing I/O data: - Each task waits for the data it needs to process Concurrency for I/O Preparing the request Time scale: 10ns
  27. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 27

    Processing I/O data: - Each task waits for the data it needs to process Concurrency for I/O Waiting for the response Time scale: 10ms
  28. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 28

    Processing I/O data: - Each task waits for the data it needs to process Concurrency for I/O Processing the response Time scale: 10ns
  29. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 29

    Processing I/O data: A Thread is idle 99.9999% of the time! How many threads do you need to keep your CPU busy? Concurrency for I/O ms ns ns
  30. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 30

    A thread is not cheap! - Thread startup time: ~1ms - Thread memory consumption: 2MB of stack - Context switching: ~100ms (depends on the OS) Having 1 million platform threads is not possible! Concurrency for I/O
  31. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 31

    How Many Threads Can I Run? ... 4065 4066 4067 4068 [0.373s][warning][os,thread] Failed to start thread "Unknown thread" - pthread_create failed (EAGAIN) for attributes: stacksize: 2048k, guardsize: 16k, detached. [0.373s][warning][os,thread] Failed to start the native thread for java.lang.Thread "Thread- 4066" Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached at java.base/java.lang.Thread.start0(Native Method) at java.base/java.lang.Thread.start(Thread.java:1451) at _3_how_many_platform_thread.printHowManyThreads(...java:19) at _3_how_many_platform_thread.main(...java:46) On a MacBook Air M1 (16GB of RAM)
  32. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 32

    CompletionState / CompletableFuture Asynchronous / Reactive programming Async / Await (C# or Kotlin) Mono / Multi (Spring) Uni / Multi (Quarkus) Solutions?
  33. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 33

    Breaking down a request handling into small stages Then compose them into a pipeline The code becomes: - hard to read and write (callback hell) - hard to debug (call stack?) - hard to test - hard to profile Solutions?
  34. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential:

    Internal/Restricted/Highly Restricted 34 Loom to the Rescue
  35. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 35

    Virtual Thread! // platform threads var pthread = new Thread(() -> { System.out.println("platform " + Thread.currentThread()); }); pthread.start(); pthread.join();
  36. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 36

    Virtual Thread! // virtual threads var vthread = Thread.startVirtualThread(() -> { System.out.println("virtual " + Thread.currentThread()); }); vthread.join(); // platform threads var pthread = Thread.ofPlatform(() -> { System.out.println("platform " + Thread.currentThread()); }); pthread.join();
  37. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 37

    A virtual thread runs on a carrier thread from a Fork-Join pool (not the common fork join pool) This pool implements a FIFO queue (instead of a LIFO one) Virtual Thread! // platform threads platform Thread[#14,Thread-0,5,main] // virtual threads virtual VirtualThread[#15]/runnable@ForkJoinPool-1-worker-1
  38. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 38

    Thread Polymorphic Builder // platform threads var pthread = Thread.ofPlatform() .name("platform-", 0) .start(() -> { System.out.println("platform " + Thread.currentThread()); }); pthread.join(); // virtual threads var vthread = Thread.ofVirtual() .name("virtual-", 0) .start(() -> { System.out.println("virtual " + Thread.currentThread()); }); vthread.join();
  39. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 39

    How many virtual threads can I run?
  40. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 40

    Platform/OS thread (starts in ms) - Creates a 2MB stack upfront - System call to ask the OS to schedule the thread Virtual thread (starts in μs) - Grow and shrink the stack dynamically - Use a specific fork-join pool of platform threads (carrier threads) - One platform thread per core Running a Thread
  41. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 41

    How does it work under the hood?
  42. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential:

    Internal/Restricted/Highly Restricted 42 Continuation
  43. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 43

    Example of Thread.sleep(): Calls Contination.yield() Where Does the Magic Come From? @ChangesCurrentThread private boolean yieldContinuation() { boolean notifyJvmti = notifyJvmtiEvents; // unmount if (notifyJvmti) notifyJvmtiUnmountBegin(false); unmount(); try { return Continuation.yield(VTHREAD_SCOPE); } finally { // re-mount mount(); if (notifyJvmti) notifyJvmtiMountEnd(false); } }
  44. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 44

    yield() copies the stack to the heap Continuation.yield() heap stack start() Platform thread 1 sleep()
  45. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 45

    run() copies from the heap to another stack (optimization: only copies the topmost stack frames) Continuation.run() stack start() Platform thread 1 heap
  46. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 46

    run() copies from the heap to another stack (optimization: only copies the topmost stack frames) Continuation.run() stack start() Platform thread 1 stack Platform thread 2 heap
  47. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 47

    jdk.internal.vm.Continuation var scope = new ContinuationScope("hello"); var continuation = new Continuation(scope, () -> { System.out.println("C1"); Continuation.yield(scope); System.out.println("C2"); Continuation.yield(scope); System.out.println("C3"); }); System.out.println("start"); continuation.run(); System.out.println("came back"); continuation.run(); System.out.println("back again"); continuation.run(); System.out.println("back again again"); Execution: start C1 came back C2 back again C3 back again again
  48. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 48

    A Platform Thread is a thin wrapper on an OS Thread A Virtual Thread is not tied to a particular OS Thread A Virtual Thread only consumes an OS Thread when it performs calculations on the CPU Running a Virtual Thread
  49. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 49

    Creating a virtual thread is cheap Blocking a virtual thread is cheap Pooling virtual threads is useless
  50. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 50

    Two strategies for concurrency - Competitive: all threads compete for the CPUs/cores - Cooperative: each thread hand of the CPUs to the next Loom implementation does both - OS schedule carrier threads to CPU/cores and - JDK codes schedule virtual threads to carrier threads Loom Internals
  51. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 51

    Most of the code of the virtual threads scheduling is written in Java in the JDK (jdk.internal.vm.Continuation) Written in C in the JVM: - Copy of the stack frames back and forth - GCs modified to find references in stack on heap Loom is not Implemented « By the JVM »
  52. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 52

    All blocking codes are changed to - Check if current thread is a virtual thread - If it is, instead of blocking: - Register a handler that will be called when the OS is ready (using NIO) - Call Continuation.yield() - When the handler is called, find a carrier thread and call Continuation.start() In the JDK
  53. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 53

    Sometimes virtual threads are pinned to their carrier thread Synchronized block are written in assembly and uses an address on the stack the stack frames can not be copied Prefer ReentrantLock over synchronized() There Are Cases Where It Does Not Work
  54. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 54

    Sometimes virtual threads are pinned to their carrier thread Native code that does an upcall to Java may use an address on stack the stack frames can not be copied There Are Cases Where It Does Not Work
  55. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 55

    Java 13 - JEP 353 Reimplement the Legacy Socket API Java 14 - JEP 373 Reimplement the Legacy Datagram Socket API - JEP 374 Deprecate and Disable Biased Locking Stealth Rewrite of the JDK for Loom
  56. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 56

    Java 18 - JEP 416 Reimplement Core Reflection with Method Handles - JEP 418 (Internet-Address Resolution SPI) in JDK 18 defined a service-provider interface for host name and address lookup. This will allow third-party libraries to implement alternative java.net.InetAddress resolvers that do not pin threads during host lookup Stealth Rewrite of the JDK for Loom
  57. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 57

    The JDK creates as many virtual threads as the user want - Mount a virtual thread to an available carrier thread when starting - If blocking, unmount the current virtual thread and mount another virtual thread Loom Idea: Under the Hood
  58. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 58

    ThreadLocal are evil! But still working ThreadLocal variables are stored in a HashMap. What about having 1 million HashMap? How can a ThreadLocal variable exist when a task can switch from one thread (virtual / platform) to the other? What About ThreadLocal?
  59. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 59

    (It was called ScopeLocal in a previous version) Runs across thread (platform / virtual) ExtentLocal to the Rescue! ExtentLocal<String> KEY = ExtentLocal.newInstance(); Runnable task = () -> System.out.printn("Key is " + KEY.get()); Runnable taskA = () -> ExtentLocal.where(KEY, "Value A").run(taskA);
  60. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential:

    Internal/Restricted/Highly Restricted 60 Structured Concurrency
  61. Coffee (or whatever) Break!

  62. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential:

    Internal/Restricted/Highly Restricted 62 Structured Concurrency
  63. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 63

    A travel agency sells travels. On the response page, it wants to display: - the quotation - the weather forecast for the destination The Travel Agency Example
  64. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 64

    The Travel Agency Example Quotation Weather Forecast Travel Page
  65. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 65

    1) If getQuotation() fails, getTravelPage() fails and getWeather() is not called Synchronous Travel Agency Page getTravelPage() { Quotation quotation = getQuotation(); // exception? Weather weather = getWeather(); // exception? return buildPage(quotation, weather); }
  66. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 66

    2) If the getTravelPage() thread is interrupted, everything is interrupted. Synchronous Travel Agency Page getTravelPage() { Quotation quotation = getQuotation(); // exception? Weather weather = getWeather(); // exception? return buildPage(quotation, weather); }
  67. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 67

    3) A stack trace shows you that you are running getQuotation(), called by getTravelPage(). Synchronous Travel Agency Page getTravelPage() { Quotation quotation = getQuotation(); // exception? Weather weather = getWeather(); // exception? return buildPage(quotation, weather); } org.paumard.loom.LoomExample.getQuotation(LoomExample.java:99) org.paumard.loom.LoomExample.getTravelPage(LoomExample.java:20) org.paumard.loom.LoomExample.main(LoomExample.java:16)
  68. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 68

    Problems: - Latency grows with the number of requests - Your CPU is used at 0.00001% Synchronous Travel Agency
  69. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 69

    ExecutorService Based Travel Agency ExecutorService service = ...; Page getTravelPage() { Future<Quotation> quotation = service.submit(() -> getQuotation()); Future<Weather> weather = service.submit(() -> getWeather()); return buildPage(quotation.get(), weather.get()); }
  70. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 70

    1) If getQuotation() fails, getTravelPage() fails, but getWeather() is still running ExecutorService Based Travel Agency Future<Quotation> quotation = service.submit(() -> getQuotation()); Future<Weather> weather = service.submit(() -> getWeather());
  71. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 71

    2) If getTravelPage() is interrupted, then getQuotation() and getWeather() don’t know it. ExecutorService Based Travel Agency Page getTravelPage() { Future<Quotation> quotation = service.submit(() -> getQuotation()); Future<Weather> weather = service.submit(() -> getWeather()); return buildPage(quotation.get(), weather.get()); }
  72. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 72

    3) What about the stack trace? ExecutorService Based Travel Agency Page getTravelPage() { Future<Quotation> quotation = service.submit(() -> getQuotation()); Future<Weather> weather = service.submit(() -> getWeather()); return buildPage(quotation.get(), weather.get()); }
  73. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 73

    Stack trace org.paumard.loom.LoomExample.getQuotation(LoomExample.java:107) org.paumard.loom.LoomExample.lambda$getTravelPage$0(LoomExample.java:28) java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1143) org.paumard.loom.LoomExample.getQuotation(LoomExample.java:110) org.paumard.loom.LoomExample.lambda$static$0(LoomExample.java:25) java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1143) org.paumard.loom.LoomJunk.getQuotation(LoomJunk.java:110) java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1143)
  74. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 74

    CompletableFuture Based Travel Agency var quotationCF = CompletableFuture.supplyAsync(() -> getQuotation()); var weatherCF = CompletableFuture.supplyAsync(() -> getWeather()); CompletableFuture<Page> travelPageCF = quotationCF.thenCompose( quotation -> weatherCF .thenApply( weather -> buildPage(quotation, weather)));
  75. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 75

    1) If getQuotation() fails, getTravelPage() fails, and getWeather() is still called CompletableFuture Based Travel Agency var quotationCF = CompletableFuture.supplyAsync(() -> getQuotation()); var weatherCF = CompletableFuture.supplyAsync(() -> getWeather());
  76. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 76

    1) If getQuotation() fails, getTravelPage() fails, you can cancel getWeather() CompletableFuture Based Travel Agency CompletableFuture<Page> travelPageCF = quotationCF .exceptionally(t -> { weatherCF.cancel(true); throw new RuntimeException(t); }) .thenCompose(...);
  77. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 77

    2) If getWeather() fails, you can handle the exception nicely CompletableFuture Based Travel Agency CompletableFuture<Page> travelPageCF = quotationCF.thenCompose( quotation -> weatherCF // .completeOnTimeout(Weather.UNKNOWN, 100, MILLISECONDS) .exceptionally(e -> Weather.UNKNOWN) .thenApply( weather -> buildPage(quotation, weather)));
  78. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 78

    CompletableFuture Based Travel Agency var quotationCF = CompletableFuture.supplyAsync(() -> getQuotation()); var weatherCF = CompletableFuture.supplyAsync(() -> getWeather()); CompletableFuture<Page> travelPageCF = quotationCF .exceptionally(t -> { weatherCF.cancel(true); throw new RuntimeException(t); }) .thenCompose( quotation -> weatherCF // .completeOnTimeout(Weather.UNKNOWN, 100, MILLISECONDS) .exceptionally(e -> Weather.UNKNOWN) .thenApply( weather -> buildPage(quotation, weather)));
  79. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 79

    3) If getTravelPage() is interrupted, you cannot interrupt getQuotation() nor getWeather() 4) And what about the stack trace? CompletableFuture Based Travel Agency
  80. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 80

    CompletableFuture Based Travel Agency org.paumard.loom.LoomExample.getQuotation(LoomExample.java:125) org.paumard.loom.LoomExample.lambda$getTravelPage$2(LoomExample.java:38) java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768) java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1760) java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) org.paumard.loom.LoomExample.getWeather(LoomExample.java:123) org.paumard.loom.LoomExample.lambda$getTravelPage$3(LoomExample.java:40) java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768) java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1760) java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
  81. Quotation Server A 5/14/2022 Copyright © 2021, Oracle and/or its

    affiliates | 81 The Travel Agency Example Quotation Server B Quotation Server C Weather Forecast Server A Weather Forecast Server B Weather Forecast Server C Travel Agency
  82. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 82

    It needs to be closed (try with resources FTW!) It creates virtual threads on demand Pattern: - Launch tasks - Call join() - Get the results Structured Scope
  83. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 83

    A StructuredTaskScope object looks like an ExecutorService - It takes tasks and run then - And returns Future But: - An executor lives with your application - A task scope lives with your tasks StructuredTaskScope
  84. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 84

    - ShutdownOnSuccess - ShutdownOnFailure Can be extended to implement specific needs StructuredTaskScope
  85. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 85

    Allows you to implement your own logic and error handling handleComplete(Future<>) is the method you need to override Extending StructuredTaskScope
  86. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 86

    Stack Trace, ThreadDumps? > jcmd <pid> Thread.dump_to_file –format-json <filename.json>
  87. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 87

    Allows you to bind elements to a scope Your tasks can read these elements, and use them Use ExtentLocal sparsely and wisely! ExtentLocal
  88. Loom is Great!