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

Structured Concurrency, Scoped Values and Joine...

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Structured Concurrency, Scoped Values and Joiners in the JDK 25 26 27

Loom already delivered virtual threads and scoped values. It still needs to deliver one last element: the Structured Concurrency API, a preview feature of JDK 26. As of now, launching requets in parallel can be done using the ExecutorService pattern, or callback-based asynchronous programming. The Structured Concurrency API gives you new patterns of code, based on virtual threads, to launch this request in parallel, following the principle of structured imperative programming. The code you write is more readable, with less places for bugs to hide. It also gives you meaningful stack traces for better debugability and profiling. This presentation takes you through these patterns, showing you how the StructuredTaskScope objects are working, and how you can configure them. You will see how you can build your own asynchronous strategies for the handling of the results your tasks give you using Joiners, and how you can build them to exactly fit the specific needs of your application.

Avatar for José

José

May 05, 2026

More Decks by José

Other Decks in Programming

Transcript

  1. The Structured Concurrency API in JDK 26 (and 27) Scoped

    Values and Joiners José Paumard Java Developer Advocate Java Platform Group
  2. Tune in! 5 Copyright © 2025, Oracle and/or its affiliates

    | Inside Java Newscast JEP Café Road To 25 series Inside.java Inside Java Podcast Sip of Java Cracking the Java coding interview
  3. OpenJDK is the place where it all happens 6 Copyright

    © 2025, Oracle and/or its affiliates | https://openjdk.org/
  4. OpenJDK is the place where it all happens 7 Copyright

    © 2025, Oracle and/or its affiliates | https://jdk.java.net/
  5. First Preview in the JDK 19 9 Copyright © 2025,

    Oracle and/or its affiliates |
  6. Virtual Threads: Final in 21 Pinning on synchronization was solved

    in 24 10 Copyright © 2025, Oracle and/or its affiliates |
  7. What is Loom About? Loom is about fixing concurrency issues

    3 issues to fix: - Blocking a platform thread is bad - Having a non-relevant stack trace is annoying - Having loose threads is a waste of resources 14 Copyright © 2025, Oracle and/or its affiliates |
  8. Virtual Threads A virtual thread is a regular Java object

    Blocking a virtual thread does not block its carrier thread Virtual threads should only be used for blocking tasks Don’t use virtual threads for long running in-memory computations! 15 Copyright © 2025, Oracle and/or its affiliates |
  9. Why GOTO are Banned? Because a GOTO can take you

    anywhere in your code When a bug occurs on a given line It’s impossible to know where the program is coming from There is no stack trace when using GOTO 18 Copyright © 2025, Oracle and/or its affiliates |
  10. Executor Running a Task in a Worker Thread 19 Copyright

    © 2025, Oracle and/or its affiliates | Instructions Launch a task in another thread More instructions Runnable
  11. Why GOTO are Banned? Running a task in another thread

    Whether it is in an ExecutorService Or through a CompletableFuture Is like using a GOTO! The information on the caller is lost Structured Concurrency fixes that 20 Copyright © 2025, Oracle and/or its affiliates |
  12. Executor Running a Task in a Worker Thread 21 Copyright

    © 2025, Oracle and/or its affiliates | Instructions Launch a task in another thread More instructions Runnable
  13. Executor What About ThreadLocal? 22 Copyright © 2025, Oracle and/or

    its affiliates | Instructions Launch a task in another thread More instructions Set a ThreadLocal
  14. Executor What About ThreadLocal? 23 Copyright © 2025, Oracle and/or

    its affiliates | Instructions Launch a task in another thread More instructions Set a ThreadLocal
  15. Executor What About ThreadLocal? 24 Copyright © 2025, Oracle and/or

    its affiliates | Instructions Launch a task in another thread More instructions Set a ThreadLocal If not removed… … it is also available here!
  16. Running Asynchronous Tasks 25 Copyright © 2025, Oracle and/or its

    affiliates | Instructions async tasks Executor
  17. Running Asynchronous Tasks 26 Copyright © 2025, Oracle and/or its

    affiliates | Instructions async tasks Executor
  18. Running Asynchronous Tasks 27 Copyright © 2025, Oracle and/or its

    affiliates | Instructions async tasks Executor
  19. Running Asynchronous Tasks 28 Copyright © 2025, Oracle and/or its

    affiliates | Instructions async tasks Executor
  20. Running Asynchronous Tasks 29 Copyright © 2025, Oracle and/or its

    affiliates | Instructions async tasks Executor
  21. Running Asynchronous Tasks 30 Copyright © 2025, Oracle and/or its

    affiliates | StructuredTaskScope Instructions async tasks
  22. Running Asynchronous Tasks 31 Copyright © 2025, Oracle and/or its

    affiliates | StructuredTaskScope Instructions async tasks
  23. Running Asynchronous Tasks 32 Copyright © 2025, Oracle and/or its

    affiliates | More instructions StructuredTaskScope close() Instructions async tasks scope.close() is called It interrupts the threads that are still running
  24. Running Asynchronous Tasks 33 Copyright © 2025, Oracle and/or its

    affiliates | StructuredTaskScope close() Instructions async tasks It a task runs forever, and does not react to the interruption Then close() blocks You can get a stack trace that helps you debug the situation
  25. Running Asynchronous Tasks 34 Copyright © 2025, Oracle and/or its

    affiliates | More instructions StructuredTaskScope close() Instructions async tasks No more loose thread in your application ☺
  26. How is Structured Concurrency Working? Structured Concurrency consists in creating

    an object 1) That you create when needed 2) That accepts tasks (Runnable, Callable) 3) That executes them in virtual threads 4) That gives you tools to analyze the results, and deal with timeouts and exceptions 5) That is closed after use 35 Copyright © 2025, Oracle and/or its affiliates |
  27. How is Structured Concurrency Working? Using a StructuredTaskScope: 36 Copyright

    © 2025, Oracle and/or its affiliates | try (var scope = StructuredTaskScope.open()) { }
  28. How is Structured Concurrency Working? Using a StructuredTaskScope: 37 Copyright

    © 2025, Oracle and/or its affiliates | try (var scope = StructuredTaskScope.open()) { var imagesSubtask = scope.fork(() -> someService.readImages()); var linksSubtask = scope.fork(() -> someService.readLinks()); }
  29. How is Structured Concurrency Working? Using a StructuredTaskScope: 38 Copyright

    © 2025, Oracle and/or its affiliates | try (var scope = StructuredTaskScope.open()) { var imagesSubtask = scope.fork(() -> someService.readImages()); var linksSubtask = scope.fork(() -> someService.readLinks()); scope.join(); // blocks until all the tasks complete }
  30. How is Structured Concurrency Working? Using a StructuredTaskScope: 39 Copyright

    © 2025, Oracle and/or its affiliates | try (var scope = StructuredTaskScope.open()) { var imagesSubtask = scope.fork(() -> someService.readImages()); var linksSubtask = scope.fork(() -> someService.readLinks()); scope.join(); var page = new Page(imagesSubtask.get(), linksSubtask.get()); // do something with page }
  31. How is Structured Concurrency Working? Using a StructuredTaskScope: 40 Copyright

    © 2025, Oracle and/or its affiliates | try (var scope = StructuredTaskScope.open()) { var imagesSubtask = scope.fork(() -> someService.readImages()); var linksSubtask = scope.fork(() -> someService.readLinks()); scope.join(); var page = new Page(imagesSubtask.get(), linksSubtask.get()); // do something with page } // scope.close() is called
  32. How is Structured Concurrency Working? The Subtask object UNAVAILABLE is

    the default state Moves to the SUCCESS or the FAILED state 42 Copyright © 2025, Oracle and/or its affiliates | sealed interface Subtask<T> { State state(); // UNAVAILABLE | SUCCESS | FAILED T get(); // get() and exception() Throwable exception(); // must be called after join() }
  33. How is Structured Concurrency Working? What if a callable throws

    an exception? FailedException wraps this exception 43 Copyright © 2025, Oracle and/or its affiliates | try (var scope = StructuredTaskScope.open()) { scope.fork(() -> { throw new HorribleException(); }); try { scope.join(); } catch (FailedException e) { ... } // ... }
  34. How is Structured Concurrency Working? What if a callable throws

    an exception? 44 Copyright © 2025, Oracle and/or its affiliates | try (var scope = StructuredTaskScope.open()) { 62: var imagesSubtask = scope.fork(() -> someService.readImages()); var linksSubtask = scope.fork(() -> someService.readLinks()); try { scope.join(); } catch (FailedException _) {} if (imagesSubtask.state() == State.FAILED) { imagesSubtask.exception().printStackTrace(); } // ... }
  35. How is Structured Concurrency Working? What if something goes wrong?

    45 Copyright © 2025, Oracle and/or its affiliates | java.lang.IllegalStateException: Boom! at org.myapp.SomeService.readImages(SomeService.java:12) at org.myapp.MyApp.lambda$main$0(MyApp.java:62) at java.base/java.util.concurrent. StructuredTaskScope$SubtaskImpl.run(StructuredTaskScope.java:893) at java.base/java.lang.VirtualThread.run(VirtualThread.java:329)
  36. STS Configuration Uses the Ruby builder pattern 46 Copyright ©

    2025, Oracle and/or its affiliates | try (var scope = StructuredTaskScope.open( joiner, // more on that in a minute configuration -> configuration.withName(...) .withThreadFactory(...) .withTimeout(...))) { // ... }
  37. STS Default Strategy Default behavior: join() throws an exception if

    a task fails, and returns null 47 Copyright © 2025, Oracle and/or its affiliates | try (var scope = StructuredTaskScope.open()) { // fork your tasks var result = scope.join(); // ... }
  38. Choosing a STS Strategy You can also pass a Joiner

    48 Copyright © 2025, Oracle and/or its affiliates | Joiner<T, R> joiner = ...; Callable<T> task = ...; try (var scope = StructuredTaskScope.open(joiner)) { // fork your tasks scope.fork(task); R result = scope.join(); // ... }
  39. Choosing a STS Strategy You can also pass a Joiner

    (JDK 27) 49 Copyright © 2025, Oracle and/or its affiliates | Joiner<T, R, R_X> joiner = ...; Callable<T> task = ...; try (var scope = StructuredTaskScope.open(joiner)) { // fork your tasks scope.fork(task); R result = scope.join(); // ... }
  40. The Joiner API Joiner is an interface that allows the

    definition of a strategy: - How a succeeding or failing task impacts the STS - What the join() returns A Joiner can decide to cancel a STS - On the submission of a task - On the completion (success / failure) of a task 50 Copyright © 2025, Oracle and/or its affiliates |
  41. The Joiner API The Joiner interface in 25 If onFork()

    or onComplete() returns true, the scope is canceled 51 Copyright © 2025, Oracle and/or its affiliates | interface Joiner<T, R> { // T: tasks, R: returned value boolean onFork(Subtask<? extends T> subtask); // called in the main thread boolean onComplete(Subtask<? extends T> subtask); // called in each VT R result() throws Throwable; // returned by join() }
  42. The Joiner API The Joiner interface in 26 If onFork()

    or onComplete() returns true, the scope is canceled 52 Copyright © 2025, Oracle and/or its affiliates | interface Joiner<T, R> { // T: tasks, R: returned value boolean onFork(Subtask<T> subtask); // called in the main thread boolean onComplete(Subtask<T> subtask); // called in each VT R result() throws Throwable; // returned by join() void onTimeout(); }
  43. The Joiner API in 27 The Joiner interface in 27

    If onFork() or onComplete() returns true, the scope is canceled 53 Copyright © 2025, Oracle and/or its affiliates | interface Joiner<T, R, R_X> { // T: tasks, R: returned value, R_X: exception boolean onFork(Subtask<T> subtask); // called in the main thread boolean onComplete(Subtask<T> subtask); // called in each VT R result() throws R_X; // returned / thrown by join() void timeout() throws R_X; }
  44. Open / Fork / Join / Close 54 Copyright ©

    2025, Oracle and/or its affiliates | 1) Create a STS: STS.open(joiner) StructuredTaskScope Main thread
  45. Open / Fork / Join / Close 55 Copyright ©

    2025, Oracle and/or its affiliates | 2) scope.fork(task) For each task: 1) Create a Subtask, state is UNAVAILABLE 2) Calls joiner.onFork(subtask) 3) Creates a VT that runs the task StructuredTaskScope Main thread
  46. Open / Fork / Join / Close 56 Copyright ©

    2025, Oracle and/or its affiliates | Subtask: UNAVAILABLE Subtask: UNAVAILABLE Subtask: UNAVAILABLE Subtask: UNAVAILABLE StructuredTaskScope 3) scope.join() (this is a blocking call) Main thread
  47. Open / Fork / Join / Close 57 Copyright ©

    2025, Oracle and/or its affiliates | StructuredTaskScope 3) scope.join() (this is a blocking call) 1) The subtask state is updated to SUCCESS Subtask: UNAVAILABLE Subtask: UNAVAILABLE Subtask: UNAVAILABLE Subtask: UNAVAILABLE Main thread
  48. Open / Fork / Join / Close 58 Copyright ©

    2025, Oracle and/or its affiliates | StructuredTaskScope 3) scope.join() (this is a blocking call) 1) The subtask state is updated to SUCCESS Subtask: UNAVAILABLE Subtask: SUCCESS Subtask: UNAVAILABLE Subtask: UNAVAILABLE Main thread
  49. Open / Fork / Join / Close 59 Copyright ©

    2025, Oracle and/or its affiliates | StructuredTaskScope 3) scope.join() (this is a blocking call) 1) The subtask state is updated to SUCCESS 2) The result is copied to the subtask 3) onComplete(subtask) is called 4) The VT dies Subtask: UNAVAILABLE Subtask: SUCCESS Subtask: UNAVAILABLE Subtask: UNAVAILABLE Main thread
  50. Open / Fork / Join / Close 60 Copyright ©

    2025, Oracle and/or its affiliates | StructuredTaskScope 3) scope.join() (this is a blocking call) Subtask: UNAVAILABLE Subtask: SUCCESS Subtask: UNAVAILABLE Subtask: UNAVAILABLE Main thread
  51. Open / Fork / Join / Close 61 Copyright ©

    2025, Oracle and/or its affiliates | StructuredTaskScope 3) scope.join() (this is a blocking call) Subtask: UNAVAILABLE Subtask: SUCCESS Subtask: UNAVAILABLE Subtask: UNAVAILABLE Main thread
  52. Open / Fork / Join / Close 62 Copyright ©

    2025, Oracle and/or its affiliates | StructuredTaskScope 3) scope.join() (this is a blocking call) 1) The subtask state is updated to FAILED Subtask: FAILED Subtask: SUCCESS Subtask: UNAVAILABLE Subtask: UNAVAILABLE Main thread
  53. Open / Fork / Join / Close 63 Copyright ©

    2025, Oracle and/or its affiliates | StructuredTaskScope 3) scope.join() (this is a blocking call) 1) The subtask state is updated to FAILED 2) The exception is copied to the subtask 3) onComplete(subtask) is called 4) The VT dies Subtask: FAILED Subtask: SUCCESS Subtask: UNAVAILABLE Subtask: UNAVAILABLE Main thread
  54. Open / Fork / Join / Close 64 Copyright ©

    2025, Oracle and/or its affiliates | StructuredTaskScope 3) scope.join() (this is a blocking call) Subtask: FAILED Subtask: SUCCESS Subtask: UNAVAILABLE Subtask: UNAVAILABLE Main thread
  55. Open / Fork / Join / Close 65 Copyright ©

    2025, Oracle and/or its affiliates | StructuredTaskScope 3) scope.join() (this is a blocking call) Subtask: FAILED Subtask: SUCCESS Subtask: UNAVAILABLE Subtask: SUCCESS Main thread
  56. Open / Fork / Join / Close 66 Copyright ©

    2025, Oracle and/or its affiliates | StructuredTaskScope 3) scope.join() (this is a blocking call) Subtask: FAILED Subtask: SUCCESS Subtask: SUCCESS Subtask: SUCCESS Main thread 1) Calls joiner.result() 2) join() returns the result
  57. Open / Fork / Join / Close 67 Copyright ©

    2025, Oracle and/or its affiliates | StructuredTaskScope 4) scope.join() returns the result Subtask: FAILED Subtask: SUCCESS Subtask: SUCCESS Subtask: SUCCESS Main thread
  58. Open / Fork / Join / Close 68 Copyright ©

    2025, Oracle and/or its affiliates | 5) Exits the try block, scope.close() is called Main thread
  59. Cancelling a STS onFork() and onComplete() can return true In

    that case the scope is immediately canceled 69 Copyright © 2025, Oracle and/or its affiliates |
  60. Cancelling a STS from onFork() 70 Copyright © 2025, Oracle

    and/or its affiliates | 2) scope.fork(task) For the next task onFork() returns true StructuredTaskScope Main thread Subtask: UNAVAILABLE Subtask: UNAVAILABLE It cancels the STS
  61. Cancelling a STS from onComplete() 71 Copyright © 2025, Oracle

    and/or its affiliates | StructuredTaskScope 3) scope.join() (this is a blocking call) onComplete() returns true Subtask: FAILED Subtask: SUCCESS Subtask: SUCCESS Subtask: UNAVAILABLE Main thread It also cancels the STS
  62. Cancelling a STS When a STS is cancelled: 1) All

    the virtual threads are interrupted 2) onComplete() is not called anymore 3) joiner.result() is called 4) sts.join() returns 72 Copyright © 2025, Oracle and/or its affiliates |
  63. Cancelling a STS Warning: some tasks may complete before their

    thread is interrupted So you may have subtasks in SUCCESS or FAILED but onComplete() was not called for them 73 Copyright © 2025, Oracle and/or its affiliates |
  64. Closing a StructuredTaskScope The call to close() waits for all

    the threads to be done If some task does not stop when its virtual thread is interrupted, then close() does not return At least you have a stack trace that takes you where it happens! 74 Copyright © 2025, Oracle and/or its affiliates |
  65. Dealing with Timeouts 75 Copyright © 2025, Oracle and/or its

    affiliates | StructuredTaskScope 3) scope.join() (this is a blocking call) Subtask: UNAVAILABLE Subtask: SUCCESS Subtask: UNAVAILABLE Subtask: FAILED Main thread
  66. Dealing with Timeouts 76 Copyright © 2025, Oracle and/or its

    affiliates | StructuredTaskScope 3) scope.join() (this is a blocking call) Subtask: UNAVAILABLE Subtask: SUCCESS Subtask: UNAVAILABLE Subtask: FAILED Main thread Time is up!
  67. Dealing with Timeouts 77 Copyright © 2025, Oracle and/or its

    affiliates | StructuredTaskScope 3) scope.join() (this is a blocking call) Subtask: UNAVAILABLE Subtask: SUCCESS Subtask: UNAVAILABLE Subtask: FAILED Main thread The default behavior is: 1) The STS is cancelled 3) It interrupts the VTs 4) joiner.onTimeout() is called, throws a TimeoutException 5) You can catch it and analyze the subtasks
  68. Dealing with Timeouts 78 Copyright © 2025, Oracle and/or its

    affiliates | StructuredTaskScope 3) scope.join() (this is a blocking call) Subtask: UNAVAILABLE Subtask: SUCCESS Subtask: UNAVAILABLE Subtask: FAILED Main thread You can set a Joiner: 1) The STS is cancelled 2) joiner.onTimeout() is called 3) If you do not to throw the TimeoutException 4) joiner.result() is called 5) scope.join() returns the result
  69. Dealing with Timeouts 79 Copyright © 2025, Oracle and/or its

    affiliates | 5) Exits the try block and closes the STS Main thread
  70. Dealing with Timeouts Default behavior: a TimeoutException is thrown by

    the call to joiner.onTimeout() so result() is not called This TimeoutException is then thrown by the call to sts.join() You can catch the TimeoutException and analyze the subtasks that you have 80 Copyright © 2025, Oracle and/or its affiliates |
  71. Dealing with Timeouts You can implement your own strategy with

    a Joiner If your joiner.onTimeout() method does not throw Then joiner.result() is called, and you can create a result from the subtasks you have 81 Copyright © 2025, Oracle and/or its affiliates |
  72. Role of a Joiner The Joiner defines the following: -

    Should the scope be cancelled or not, and when - How to compute a result - What to do with exceptions and timeout It works with: - The result or the exception of each task - A possible timeout 82 Copyright © 2025, Oracle and/or its affiliates |
  73. Available Joiners Factory methods from the Joiner interface: 83 Copyright

    © 2025, Oracle and/or its affiliates | Joiner<T, T> anySuccessfulOrThrow(); Joiner<T, List<Subtask<T>> allSuccessfulOrThrow(); Joiner<T, Void> awaitAllSuccessfulOrThrow(); Joiner<T, Void> awaitAll(); Joiner<T, List<Subtask<T>>> allUntil(predicate); Predicate<Subtask<T>> predicate = ...; // returns true cancel the scope
  74. Implementing Your Own Joiner You need to be careful with

    race conditions Example: extracting a max 84 Copyright © 2025, Oracle and/or its affiliates | class MaxJoiner<T extends Comparable<? super T>> implements Joiner<T, T> { final List<Subtask<? extends T>> subtasks = new ArrayList<>(); public boolean onFork(Subtask<? extends T> subtask) { this.subtasks.add(subtask); return false; } }
  75. Implementing Your Own Joiner You need to be careful with

    race conditions Example: extracting a max 85 Copyright © 2025, Oracle and/or its affiliates | class MaxJoiner<T extends Comparable<? super T>> implements Joiner<T, T> { final List<Subtask<? extends T>> subtasks = new ArrayList<>(); public Optional<T> result() { return subtasks.stream() .filter(subtask -> subtask.state() == State.SUCCESS) .map(Subtask::get) .max(Comparator.naturalOrder()); } }
  76. Implementing Your Own Joiner You need to be careful with

    race conditions Example: extracting a max 86 Copyright © 2025, Oracle and/or its affiliates | class MaxJoiner<T extends Comparable<? super T>> implements Joiner<T, T> { final List<T> subtasks ; boolean onComplete(Subtask<? extends T> subtask) { if (subtask.state() == State.SUCCESS) { this.subtasks.add(subtask.get()); } return false; } }
  77. Implementing Your Own Joiner You need to be careful with

    race conditions Example: extracting a max 87 Copyright © 2025, Oracle and/or its affiliates | class MaxJoiner<T extends Comparable<? super T>> implements Joiner<T, T> { final List<T> subtasks = new ArrayList<>(); boolean onComplete(Subtask<? extends T> subtask) { // executed in a VT!! if (subtask.state() == State.SUCCESS) { this.subtasks.add(subtask.get()); } return false; } }
  78. Implementing Your Own Joiner You need to be careful with

    race conditions Example: extracting a max 88 Copyright © 2025, Oracle and/or its affiliates | class MaxJoiner<T extends Comparable<? super T>> implements Joiner<T, T> { final Collection<T> subtasks = new ConcurrentLinkedQueue<>(); boolean onComplete(Subtask<? extends T> subtask) { if (subtask.state() == State.SUCCESS) { this.subtasks.add(subtask.get()); } return false; } }
  79. Implementing Your Own Joiner You need to be careful with

    race conditions Example: extracting a max Think twice before doing anything in onComplete() 89 Copyright © 2025, Oracle and/or its affiliates |
  80. Scoped Values The goal of Scoped Values is to fix

    the issues ThreadLocal have 91 Copyright © 2025, Oracle and/or its affiliates |
  81. What is Wrong with ThreadLocal? So many things… - They

    are mutable - The VM cannot optimize them - They may be kept alive forever Virtual Threads support ThreadLocal variables! 92 Copyright © 2025, Oracle and/or its affiliates |
  82. Welcome to ScopedValue! ScopedValues are non-modifiable They are not bound

    to a particular thread They are bound to a single method call 93 Copyright © 2025, Oracle and/or its affiliates | static final ScopedValue<String> KEY = ScopedValue.newInstance();
  83. Welcome to ScopedValue! ScopedValues are non-modifiable They are not bound

    to a particular thread They are bound to a single method call 94 Copyright © 2025, Oracle and/or its affiliates | static final ScopedValue<String> KEY = ScopedValue.newInstance(); ... ScopedValue.where(KEY, "Value_1") .run(() -> doSomethingSmart()));
  84. Welcome to ScopedValue! ScopedValues are non-modifiable They are not bound

    to a particular thread They are bound to a single method call 95 Copyright © 2025, Oracle and/or its affiliates | static final ScopedValue<String> KEY = ScopedValue.newInstance(); ... ScopedValue.where(KEY, "Value_1") .where(OTHER_KEY, "Value_2") .run(() -> doSomethingSmart()));
  85. Welcome to ScopedValue! ScopedValues are non-modifiable They are not bound

    to a particular thread They are bound to a single method call 96 Copyright © 2025, Oracle and/or its affiliates | void doSomethingSmart() { if (KEY.isBound()) { String value = KEY.get(); ... } else { throw new IllegalStateException("Key is not bound"); } }
  86. How Do Scoped Values Work? Each stack has a reference

    to the thread running it 97 Copyright © 2025, Oracle and/or its affiliates | Stack main()
  87. How Do Scoped Values Work? Each thread has a reference

    to an empty Snapshot A Snapshot can store bindings between keys and values 98 Copyright © 2025, Oracle and/or its affiliates | Stack main() Snapshot
  88. How Do Scoped Values Work? The new bindings are stored

    in a new snapshot Snapshots form a linked list 99 Copyright © 2025, Oracle and/or its affiliates | void main() { ScopedValue .where(K1, "V1") .where(K2, "V2") .run(() -> readPage())); } Snapshot K1 V1 K2 V2 Stack main() Snapshot
  89. How Do Scoped Values Work? When you read a binding

    You explore the linked list But reading a linked list is slow → you need a cache 100 Copyright © 2025, Oracle and/or its affiliates | void readPage() { var v1 = K1.get(); } Snapshot K1 V1 K2 V2 Stack main() Snapshot readpage()
  90. How Do Scoped Values Work? When you read a binding

    You explore the linked list But reading a linked list is slow -> you need a cache 101 Copyright © 2025, Oracle and/or its affiliates | void readPage() { var v1 = K1.get(); } Snapshot K1 V1 K2 V2 Stack main() Snapshot readpage() K1
  91. How Do Scoped Values Work? When a binding is read

    1) You check the cache 2) If it is not found then you explore the linked list 3) You populate the cache 102 Copyright © 2025, Oracle and/or its affiliates | void readPage() { var v1 = K1.get(); } Snapshot K1 V1 K2 V2 Stack main() Snapshot readpage() K1
  92. Snapshot K1 V1 K2 V2 How Do Scoped Values Work?

    103 Copyright © 2025, Oracle and/or its affiliates | void readPage() { var v1 = K1.get(); var v2 = K2.get(); } Stack main() Snapshot readpage() K2 K1
  93. Snapshot K1 V1 K2 V2 How Do Scoped Values Work?

    All the subsequent reads for K1 and K2 can get the value from the cache 104 Copyright © 2025, Oracle and/or its affiliates | void readPage() { var v1 = K1.get(); var v2 = K2.get(); } Stack main() Snapshot readpage() K2 K1
  94. How Do Scoped Values Work? When you create another binding

    You remove the keys from the cache 105 Copyright © 2025, Oracle and/or its affiliates | Stack main() void readPage() { var v1 = K1.get(); var v2 = K2.get(); ScopedValue .where(K1, "V10") .run(() -> readLinks())); } Snapshot Snapshot K1 V1 K2 V2 readpage() K2
  95. How Do Scoped Values Work? When you create another binding

    You remove the keys from the cache, and add a Snapshot 106 Copyright © 2025, Oracle and/or its affiliates | Stack main() void readPage() { var v1 = K1.get(); var v2 = K2.get(); ScopedValue .where(K1, "V10") .run(() -> readLinks())); } Snapshot Snapshot K1 V1 K2 V2 readpage() K2 Snapshot K1 V10
  96. How Do Scoped Values Work? The binding are ordered from

    the youngest to the oldest So a rebinding always takes precedence 107 Copyright © 2025, Oracle and/or its affiliates | Stack main() void readLinks() { var v1 = K1.get(); var v2 = K2.get(); } Snapshot Snapshot K1 V1 K2 V2 readPage() readLinks() Snapshot K1 V10 K2 K1
  97. How Do Scoped Values Work? When returning 1) The keys

    are removed 108 Copyright © 2025, Oracle and/or its affiliates | void readPage() { var v1 = K1.get(); ScopedValue .where(K1, "V10") .run(() -> readLinks())); IO.println("Done"); } Stack main() Snapshot Snapshot K1 V1 K2 V2 readPage() readLinks() Snapshot K1 V10 K2
  98. How Do Scoped Values Work? When returning 1) The keys

    are removed 2) The Snapshot is removed 109 Copyright © 2025, Oracle and/or its affiliates | Stack main() void readPage() { var v1 = K1.get(); ScopedValue .where(K1, "V10") .run(() -> readLinks())); IO.println("Done"); } Snapshot Snapshot K1 V1 K2 V2 readPage() K2
  99. How Do Scoped Values Work? Subsequent reads can repopulate the

    cache 110 Copyright © 2025, Oracle and/or its affiliates | Stack main() void readPage() { var v1 = K1.get(); ScopedValue .where(K1, "V10") .run(() -> readLinks())); IO.println("Done"); var v2 = K1.get(); } Snapshot Snapshot K1 V1 K2 V2 readPage() K2 K1
  100. How Do Scoped Values Work? In the end the cache

    is empty, and all the Snapshots are removed 111 Copyright © 2025, Oracle and/or its affiliates | Stack main() void main() { ScopedValue .where(K1, "V1") .where(K2, "V2") .run(() -> readPage())); IO.println("Done"); } Snapshot
  101. ScopedValue If your task launches other tasks in other threads,

    are the scoped values visible from these other tasks? - It could be threads created by your task - Or existing threads 112 Copyright © 2025, Oracle and/or its affiliates |
  102. Executor Are Scoped Values Sent to Executors? 113 Copyright ©

    2025, Oracle and/or its affiliates | Instructions Bind a scoped value More instructions Runnable
  103. Executor Are Scoped Values Sent to Executors? 114 Copyright ©

    2025, Oracle and/or its affiliates | Instructions Bind a scoped value More instructions This could escape the scope of your task Runnable
  104. Executor Are Scoped Values Sent to Executors? 115 Copyright ©

    2025, Oracle and/or its affiliates | Instructions Bind a scoped value More instructions This could escape the scope of your task = possible loose binding! Runnable
  105. Executor Are Scoped Values Sent to Executors? 116 Copyright ©

    2025, Oracle and/or its affiliates | Instructions Bind a scoped value More instructions Scoped value bindings are not available here Runnable
  106. Bind a scoped value Are Scoped Values Sent to VTs

    in STS? 117 Copyright © 2025, Oracle and/or its affiliates | More instructions Instructions async tasks StructuredTaskScope No loose binding is possible
  107. Bind a scoped value Are Scoped Values Sent to VTs

    in STS? 118 Copyright © 2025, Oracle and/or its affiliates | More instructions Instructions async tasks StructuredTaskScope No loose binding is possible = Your async tasks can see the Scoped Value bindings
  108. ScopedValue Scoped Values are not transmitted to threads reason: you

    don’t want loose scoped values They are transmitted to StructuredTaskScope reason: you can’t have loose scoped values 119 Copyright © 2025, Oracle and/or its affiliates |
  109. Loom: Where Are We? Virtual Threads Final in 21, improved

    in 24 Scoped Values Final in 25 StructuredConcurrency Preview in 25, 26, 27 120 Copyright © 2025, Oracle and/or its affiliates |
  110. Devoxx FR 26 Loom Lab Work on this application yourself!

    121 Copyright © 2025, Oracle and/or its affiliates |