Skip to content

Commit 07addbf

Browse files
committed
JDK 25: update notes and example
1 parent 7a609c0 commit 07addbf

6 files changed

+465
-12
lines changed

README.md

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,22 @@ A project to explore more about the new features from Java 8 through Java 21.
1111

1212
## Resume by Version
1313

14+
`standard` shows the version in which a preview feature became stable and available for production use.
15+
1416
* [Java 25](java-25/)
15-
* Stable Values (Preview)
17+
* Stable Values (preview)
1618
* Remove the 32-bit x86 Port
17-
* Module Import Declarations
18-
* Compact Source Files and Instance Main Methods
19-
* Flexible Constructor Bodies
2019
* Structured Concurrency (preview 5)
20+
* Scoped Values (standard) :rocket:
21+
* Primitive Types in Patterns, instanceof, and switch (preview 3)
22+
* Key Derivation Function API (standard)
23+
* Module Import Declarations (standard)
24+
* Compact Source Files and Instance Main Methods (standard) :rocket:
25+
* Flexible Constructor Bodies (standard)
26+
* Ahead-of-Time Command-Line Ergonomics
27+
* Ahead-of-Time Method Profiling
28+
* Compact Object Headers (standard) :rocket:
29+
* Generational Shenandoah (standard)
2130

2231
* [Java 24](java-24/) (Mar, 2025)
2332
* Generational Shenandoah (experimental)
@@ -27,20 +36,18 @@ A project to explore more about the new features from Java 8 through Java 21.
2736
* Key Derivation Function API (preview)
2837
* Remove the Windows 32-bit x86 Port
2938
* Ahead-of-Time Class Loading & Linking
30-
* Class-File API
31-
* Steam Gatherers
39+
* Class-File API (standard)
40+
* Steam Gatherers (standard)
3241
* Permanently Disable the Security Manager
3342
* Scoped Values (preview 4)
3443
* Primitive Types in Patterns, instanceof, and switch (preview 2)
3544
* Vector API (incubator)
3645
* ZGC Remove the Non-Generational Mode
37-
* Synchronize Virtual Threads without Pinning
46+
* Synchronize Virtual Threads without Pinning :rocket:
3847
* Flexible Constructor Bodies (preview 3)
3948
* Linking Run-Time Images without JMODs
4049
* Module Import Declarations (preview 2)
4150
* Simple Source Files and Instance Main Mathods (preview 4)
42-
* Quantum-Resistant Module-Lattice-Based Key Encapsulation Mechanism
43-
* Quantum-Resistant Module-Lattice-Based Digital Signature Algorithm
4451
* Warn Upon Use of Memory-Access Methods in sun.misc.Unsafe
4552
* Structured Concurrency (preview 4)
4653
* Deprecate the 32-bit x86 Port for Removal
@@ -63,7 +70,7 @@ A project to explore more about the new features from Java 8 through Java 21.
6370
* Region Pinning for G1
6471
* Statements before `super` (preview)
6572
* FFM API (standard)
66-
* Unnamed Variable & Patterns
73+
* Unnamed Variable & Patterns (standard)
6774
* Class-File API (preview)
6875
* Launch Multi-File Source-Code Programs
6976
* String Templates (preview 2)
@@ -122,15 +129,15 @@ A project to explore more about the new features from Java 8 through Java 21.
122129
* Strongly Encapsulate JDK Internals
123130
* Pattern matching for `switch` (preview)
124131
* Remove RMI Activation
125-
* Sealed Classes (standard)
132+
* Sealed Classes (standard) :rocket:
126133
* Remove the experimental AOT and JIT compiler
127134
* Deprecate the Security Manager for Removal
128135
* Foreign Function & Memory API (incubator)
129136
* Vector API (fourth incubator)
130137
* Context-Specific Deserialization Filters
131138

132139
* [Java 16](java-16/) (Mar, 2021)
133-
* Records (standard)
140+
* Records (standard) :rocket:
134141
* Pattern matching for `instanceof` (standard)
135142
* Sealed classes (preview 2)
136143
* Unix-Domain Socket Channels

java-25/README.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,139 @@ To run each example use: `java --enable-preview --source 25 <FileName.java>`
2626

2727
* **Primitive Types in Patterns, instance of and switch**
2828
* re-preview without change
29+
* **Structured Concurrency**
30+
* re-preview with several API changes
31+
* previous changes in [JDK 19](../java-19/README.md) and [JDK 21](../java-21/README.md)
32+
* Joiners
33+
* introduce the concept of execution policy with `Joiner`
34+
* Joiner object handles subtask completion and produces the outcome for the `join()` method
35+
* depending on the joiner, the `join()` method may return a result, a stream of elements, or some other object
36+
* the result of `join()` is useful when we don't handle each subtask individually, rather we want to wait for all subtasks to finish and then process the results (first result or all)
37+
* a joiner instance should never be reused, always create a new instance
38+
* `StructuredTaskScope` is open using static factory methods:
39+
* `StructuredTaskScope.open()`: creates a scope with joiner strategy default of `StructuredTaskScope.Joiner.allSuccessfulOrThrow()` but `join()` will return null
40+
* `StructuredTaskScope.open(StructuredTaskScope.Joiner)`: creates a scope with the specified joiner strategy
41+
* `StructuredTaskScope.open(StructuredTaskScope.Joiner, Function)`: creates a scope with the specified joiner strategy and a function to customize the default configuration of the execution (name, thread factory and timeout)
42+
* the scope can now be configured with a instace of [`Config`](https://download.java.net/java/early_access/loom/docs/api/java.base/java/util/concurrent/StructuredTaskScope.Config.html)
43+
* `withName(String)`: sets the name of the scope to be monitored and managed
44+
* `withThreadFactory(ThreadFactory)`: sets the thread factory to use for each subtask
45+
* `withTimeout(Duration)`: sets the timeout for the scope, should use `Instant` to calculates the deadline (`Duration.between(Instant.now(), deadline)`)
46+
* `Joiner` interface declares factory methods to create joiners for some common cases
47+
* interface:
48+
```java
49+
public interface Joiner<T, R> {
50+
public default boolean onFork(Subtask<? extends T> subtask);
51+
public default boolean onComplete(Subtask<? extends T> subtask);
52+
public R result() throws Throwable;
53+
}
54+
```
55+
* factory methods for default policies:
56+
* `allSuccessfulOrThrow`:
57+
* waits for all tasks to complete successfully or throws an exception if any subtask fails
58+
* `join()` will return a stream of `Subtask` that yields the subtask results in the order they are completed
59+
* useful when all subtasks return a result of same type and we want to process them all
60+
* `anySuccessfulResultOrThrow`:
61+
* waits for any subtask to complete successfully or throws an exception if all tasks fail
62+
* `join()` will return the result of a successful subtask
63+
* useful when we want to process the result of the first successful subtask
64+
* `awaitAllSuccessfulOrThrow()`:
65+
* waits for all tasks to complete successfully or throws an exception if any subtask fails
66+
* `join()` returns `Void`
67+
* useful when the subtasks return a result of different types and we process each subtask submitted with `fork` method individually
68+
* `awaitAll`:
69+
* waits for all tasks to complete, regardless of success or failure
70+
* `join()` returns `Void`
71+
* useful when the subtasks make use of side effects operations and don't return a result or exception
72+
* each subtask will yield the result or exception of its execution
73+
* `allUntil(Predicate)`
74+
* waits all subtasks are completed or the predicate is satisfied to cancel the scope
75+
* `join()` returns stream of all subtasks in the order they were forked
76+
* each subtask can have the following states: `SUCCESS`, `FAILURE` or `UNAVAILABLE` (if the scope has been cancelled before it were forked or completed)
77+
* predicate is of type [`Predicate<StructuredTaskScope.Subtask<? extends T>>`](https://download.java.net/java/early_access/loom/docs/api/java.base/java/util/function/Predicate.html)
78+
* each subtask that is completed successfully or failed will be passed to the predicate
79+
* if the predicate returns true, the scope will be cancelled
80+
* if throws an exception, `Thread.UncaughtExceptionHandler.uncaughtException` will be called
81+
* we can implement our own joiner by implementing the interface `StructuredTaskScope.Joiner`
82+
* we can implement the method `join` to return a result of type `R` and the method `onCompletion` to handle the completion of each subtask
83+
* change in thread-dump to show virtual threads used by a `StructuredTaskScope`
84+
* `jcmd <pid> Thread.dump_to_file -format=json <file>`
85+
* **Scoped Values**
86+
* promotion to standard with minor change
87+
* `ScopedValue.orElse` method no longer accepts null as its argument
88+
* enable the sharing of immutable data within a thread and its child threads
89+
* it were inspired by the way that Lisp dialects provide support for dyanamically scoped free variables
90+
* they are preferred to thread-local variables (specially when using virtual threads)
91+
* goals:
92+
* provide a programming model to share data both within a thread and with child threads
93+
* make the lifetime of shared data visible from structure of code
94+
* ensure that data shared by a caller can be retrieved only by legitimate callees
95+
* thread shared data as immuatable so as to allow sharing by a large number of threads and to enable runtime otimiziations
96+
* problems with thread-local variables:
97+
* mutability: any object can update the variable if it has access to it
98+
* unbounded lifetime: it is stored until the thread is destroyed, if is used in a pool it can take a long time
99+
* expensive inheritance: a child thread must allocate memory for every variable stored in the parent thread
100+
* a scoped value allows data to be safely and efficiently shared between threads within same hierarchy
101+
* `scoped value is a container object that allows a data value to be safely and efficiently shared by a method with its direct and indirect callees within the same thread, and with child threads, without resorting to method parameters`
102+
* ScopedValue API works by executing a method with a `ScopedValue` object bound to some value for the bounded period of execution of a method
103+
* we use an attribute of type [`ScopedValue`](https://download.java.net/java/early_access/loom/docs/api/java.base/java/lang/ScopedValue.html) declared as final static
104+
* scoped value has multiple values associated with it, one per thread
105+
* once the value is written to scoped value it becomes immutable and is available only for a bounded period during execution of that thread insided that scope
106+
* we can create another scope when inside a scope with another variable to create a new scope
107+
* we can create nested scope with rebinded value when using the same `ScopedValue` to create a new scope
108+
* the created scope will have the new value binded
109+
* the current scope will still have its original value
110+
* usage:
111+
* first we need to create a scope key
112+
* `private static final ScopedValue<String> SCOPE_KEY = ScopedValue.newInstance();`
113+
* then we use `ScopedValue.where` to bind a value to the scope key
114+
* `ScopedValue.where(SCOPE_KEY, "my-value")`
115+
* if we bind a value to a scope key that is already bound, the new value will be used in the current scope (rebinding)
116+
* we can set multiples values to the same scope by using the same `ScopedValue` in multiple calls to `where`
117+
* `ScopedValue.where(SCOPE_KEY, "my-value").where(SCOPE_KEY_2, "other-value").run(() -> {})`
118+
* then use the method `run` or `call` to bind the scoped value with the current thread and the execution scope
119+
* method `run` is an one-way sharing to the execution scope
120+
* `run` has parameter of type `Runnable`
121+
* method `call` allow us to receive a result from the execution scope
122+
* `call` has parameter of type functional interface: `ScopedValue.CallableOp<T, X extends Throwable>`
123+
* `call` will return the result of the execution of the lambda expression
124+
* during the lifetime of the lambda expression (and every method called by it) we can read the scoped value using the method `get`
125+
* we use scoped key to read: `SCOPE_KEY.get()`
126+
* `NoSuchElementException` if the scoped value is not bounded
127+
* we can use `orElse` to set a default value if the scoped value is not bounded
128+
* `SCOPE_KEY.getOrElse("default-value")`
129+
* ex.:
130+
* `run`:
131+
```java
132+
public final static ScopedValue<String> PRINCIPAL = ScopedValue.newInstance();
133+
ScopedValue.where(PRINCIPAL, "guest")
134+
.run(() -> {
135+
var userRole = PRINCIPAL.get();
136+
});
137+
```
138+
* `call`:
139+
```java
140+
public final static ScopedValue<String> PRINCIPAL = ScopedValue.newInstance();
141+
var userRole = ScopedValue.where(PRINCIPAL, "guest")
142+
.call(() -> {
143+
return "Value: " + PRINCIPAL.get();
144+
});
145+
```
146+
* ex.:
147+
*
148+
```java
149+
var universeAnswer = ScopedValue.where(SUBJECT, "Deep Thought")
150+
.call(() -> {
151+
// some magic
152+
return 42;
153+
});
154+
```
155+
* virtual thread and cross-thread sharing
156+
* to share data between a thread and its child thread we need to make the scoped values inherited by child thread
157+
* we can use the Structured Concurrency API
158+
* the class `StructuredTaskScope` automatically make the scoped value inherited by child thread
159+
* the fork must occurrs inside the `ScopedValue` method `run`/`call`
160+
* the binding will remains until the child thread are finished and we can use `StructuredTaskScoped.join` to ensure that the child threads will terminate before `run`/`call`
161+
* when we try to access a scoped value not shared with current thread an exception `NoSuchElementException` will be thrown
29162
* **Module Import Declarations**
30163
* promotion to standard without change
31164
* allow import all packages exported by a module
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import java.util.List;
2+
import java.util.concurrent.*;
3+
4+
/**
5+
* Run: `java ScopedValueExampleVsThreadLocal.java`
6+
*/
7+
public class ScopedValueExampleVsThreadLocal {
8+
// pretty much the same
9+
final static ThreadLocal<String> USER_LOCAL = new ThreadLocal<>();
10+
final static ScopedValue<String> USER_SCOPE = ScopedValue.newInstance();
11+
12+
13+
public static void main(String[] args) {
14+
var users = List.of(
15+
"Neo",
16+
"Trinity",
17+
"Morpheus",
18+
"Switch",
19+
"Dozer"
20+
);
21+
22+
var calculator = new Calculator();
23+
var worker = new UserWorker(calculator);
24+
25+
// now ExecutorService extends AutoCloseable
26+
try (var executor = Executors.newThreadPerTaskExecutor(Thread.ofPlatform().factory())) {
27+
System.out.println("Starting processing");
28+
29+
for (var i = 0; i < 5; i++) {
30+
var user = users.get(i);
31+
32+
System.out.printf("Starting worker user %s%n", user);
33+
executor.submit(() -> {
34+
// ThreadLocal just set
35+
USER_LOCAL.set(user);
36+
37+
// with ScopedValue, we set the value and define the scope in which it will be available
38+
ScopedValue.where(USER_SCOPE, user)
39+
.run(worker::run);
40+
41+
var userLocal = USER_LOCAL.get();
42+
System.out.printf("User is still available in ThreadLocal: %s%n", userLocal);
43+
});
44+
}
45+
}
46+
47+
System.out.println("Processing ended");
48+
}
49+
}
50+
51+
class UserWorker implements Runnable {
52+
private Calculator calculator;
53+
54+
public UserWorker(Calculator calculator) {
55+
this.calculator = calculator;
56+
}
57+
58+
public void run() {
59+
System.out.printf("Getting user for calculation...%n");
60+
// both we retrieve the value in the same way
61+
var user = ScopedValueExampleVsThreadLocal.USER_SCOPE.get();
62+
var userLocal = ScopedValueExampleVsThreadLocal.USER_LOCAL.get();
63+
64+
System.out.printf("Users - ScopedValue: %s - ThreadLocal: %s%n", user, userLocal);
65+
if (!user.equals(userLocal)) {
66+
System.out.printf("Users from ScopedValue and ThreadLocal aren't the same%n", user);
67+
return;
68+
}
69+
// the first difference, in ThreadLocal we can change the value at any time
70+
ScopedValueExampleVsThreadLocal.USER_LOCAL.set(userLocal + " -- was changed");
71+
72+
System.out.printf("User %s - calculating...%n", user);
73+
var answer = this.calculator.calculate();
74+
System.out.printf("User %s - answer: %d%n", user, answer);
75+
}
76+
}
77+
78+
class Calculator {
79+
public int calculate() {
80+
// now the ThreadLocal will see a different value
81+
// (could be change anywhere, it require us to look for the points that is changing it)
82+
var user = ScopedValueExampleVsThreadLocal.USER_SCOPE.get();
83+
var userLocal = ScopedValueExampleVsThreadLocal.USER_LOCAL.get();
84+
85+
System.out.printf("Users - ScopedValue: %s - ThreadLocal: %s%n", user, userLocal);
86+
// pretend to go to DB to sum the rows
87+
try {
88+
Thread.sleep(500);
89+
} catch (InterruptedException ex) {}
90+
return user.length();
91+
}
92+
}

0 commit comments

Comments
 (0)