-
Notifications
You must be signed in to change notification settings - Fork 0
Java 20
Release Date: March 21, 2023 | Type: Standard (non-LTS) | Status:
⚠️ End of Life
Java 20 is a "consolidation" release — no brand-new headline feature, but it refines Record Patterns, re-previews Virtual Threads and Structured Concurrency with API improvements, and introduces Scoped Values as a modern, virtual-thread-friendly alternative to ThreadLocal.
flowchart LR
A[Java 19: Virtual Threads Preview] --> B[Java 20: Virtual Threads 2nd Preview]
C[Java 19: Structured Concurrency Incubator] --> D[Java 20: 2nd Incubator]
E[Java 19: Record Patterns Preview] --> F[Java 20: 2nd Preview]
G[NEW] --> H[Scoped Values Incubator]
What: An immutable alternative to ThreadLocal, designed to work efficiently with millions of virtual threads. Data is shared for the duration of a call, then automatically un-shared — no leaks, no mutation surprises.
// ThreadLocal — mutable, can leak across thread-pool reuse, expensive with virtual threads
private static final ThreadLocal<String> CURRENT_USER = new ThreadLocal<>();
CURRENT_USER.set("alice");
try {
process();
} finally {
CURRENT_USER.remove(); // ⚠️ easy to forget — causes leaks
}
// Scoped Values — immutable, automatically bounded, virtual-thread-friendly
private static final ScopedValue<String> CURRENT_USER = ScopedValue.newInstance();
ScopedValue.where(CURRENT_USER, "alice").run(() -> {
process(); // CURRENT_USER.get() == "alice" ONLY within this block
});
// Automatically un-bound here — no cleanup code needed, no leak possibleflowchart TD
A["ThreadLocal.set()"] --> B["Value persists until\nexplicitly removed"]
B --> C{"Thread reused\nfrom pool?"}
C -->|Forgot remove| D["❌ Leaked value\nseen by next task"]
E["ScopedValue.where().run()"] --> F["Value bound ONLY\nfor the lambda's duration"]
F --> G["Automatically unbound\nwhen lambda returns"]
G --> H["✅ No leak possible,\nno manual cleanup"]
Why it matters for virtual threads: ThreadLocal values get copied/inherited in ways that don't scale well to millions of virtual threads. ScopedValue was purpose-built to be cheap and safe at that scale.
Refinements to the Java 19 preview — adds support for record patterns in switch (not just instanceof):
record Point(int x, int y) {}
record Line(Point start, Point end) {}
static String describe(Object shape) {
return switch (shape) {
case Line(Point(var x1, var y1), Point(var x2, var y2))
when x1 == x2 -> "Vertical line";
case Line(Point s, Point e) -> "Line from " + s + " to " + e;
case Point(var x, var y) -> "Point at (" + x + ", " + y + ")";
default -> "Unknown shape";
};
}API refinements based on Java 19 feedback — no major conceptual changes. See the Java 19 guide for the core concept, standardized fully in Java 21.
// API stabilizing toward what ships in Java 21+
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
var user = scope.fork(() -> fetchUser(id));
var prefs = scope.fork(() -> fetchPreferences(id));
scope.join().throwIfFailed();
return new Profile(user.resultNow(), prefs.resultNow());
}// Continued refinement toward Java 22 standardization
try (Arena arena = Arena.ofConfined()) {
MemorySegment segment = arena.allocate(100);
segment.set(ValueLayout.JAVA_INT, 0, 42);
int value = segment.get(ValueLayout.JAVA_INT, 0);
}✅ If you're already using ThreadLocal for request-scoped data (user context, trace IDs), start planning a migration path to ScopedValue once it standardizes
✅ Keep experimenting with virtual threads and structured concurrency — Java 20 is the last "preview lap" before Java 21 LTS standardizes both
❌ Don't adopt Java 20 in production for its own sake — it's a non-LTS release with a short support window; wait for Java 21 unless you need a specific preview feature today