The most significant new feature in JDK 21, the latest LTS OpenJDK, is Virtual Threads. Virtual threads enable massively greater scalability of applications by using a many-to-one mapping.
JDK 21 has been released, and for binary distributions of OpenJDK, it is a long-term support (LTS) release. This means it will continue to be supported with regular security and bug-fix updates for many years, depending on your distribution. This will be at least eight years for the Zulu builds from Azul.
JDK 21 is an exciting release for developers and end-users because of its new features. With so much to cover, we’ll limit ourselves in this blog post to just what’s in JDK 21, not everything that has been added since the last LTS release, JDK 17.
Virtual Threads finally become a full-platform feature
The most significant new feature is Virtual Threads (JEP 444). This has been a preview feature since JDK 19 but is now a full platform feature.
From the beginning, Java had separate concurrent threads of execution built into the language through the fundamental Thread class. Many enterprise applications that support large numbers of simultaneous client interactions use the well-known thread-per-request programming model. As the name suggests, each client connection is allocated its own thread to handle that client’s work, being both isolated from and able to execute concurrently with all other connections. This works well but is unable to scale to very large numbers of connections due to the one-to-one mapping of Java threads to the underlying operating system platform threads. Each platform thread requires 2MB of memory just to provide a stack and core functionality. If you wanted to support a million users, you’d need 2TB of memory!
Another prevalent feature of these types of applications is that they spend most of their time blocked, waiting for operations like database access, file reads, or network connections to complete.
Virtual threads enable massively greater scalability of these applications by using a many-to-one mapping. While still using the Thread class, many virtual threads share a single platform thread. When a virtual thread sleeps for IO, the JVM can context switch to a different thread that is ready to run. Upon completion of its IO operation, the initial thread is made runnable again and can be placed back on a platform thread when one becomes available.
Although this does indeed increase the scalability of these types of applications, it should not be considered a magic bullet and used everywhere. It is essential to understand that, unlike the way the operating system manages processes, there is no time-sharing of virtual threads. If the work a thread is required to do is CPU intensive rather than IO bound, using virtual threads may result in degraded rather than improved scalability and performance.
JDK 21 also contains numerous other features that will appeal to developers:
- Sequenced collections (JEP 431): Although the standard class libraries already contain collections with a defined sequence (a List, for example), there is no common type for these. This small change to the Java API introduces a new interface, SequencedCollection, implemented by all collections with a defined order (Deque, SortedSet, etc.). This will simplify certain code constructs.
- String Templates (JEP 430): Included as a preview feature, these allow much simpler construction of strings that require variable components included in them. In addition to simple value insertion, templates can include more complex evaluations, such as arithmetic operations or calling methods directly.
- Greater Use of Patterns (JEP 440, 441 and 443): Since JDK 14, we have seen a gradual delivery of pattern-matching features. Pattern matching as a programming concept has been around since the late 1960s. The features being added build on other newer Java features like records and sealed classes.
In JDK 21, record patterns become a full feature. An example of a deconstruction pattern, the contents of a record can be accessed directly through pattern variables.
Pattern matching for switch also becomes a full feature, enabling us to switch on a type and provide access via a pattern variable.
Finally, we have the introduction of unnamed patterns and variables (again as a preview feature). Where pattern variables are not being used, we can enhance readability by acknowledging their presence (but not their details) with a single underscore.
- Structured Concurrency and Scoped Values (JEP 453 and 446): Along with virtual threads, these are both part of the larger OpenJDK Project Loom.
Structured concurrency provides a way of grouping together sub-tasks created as separate threads in a way that they can be guaranteed to all complete or fail predictably. Unfinished sub-tasks are terminated gracefully, and exception handling is dealt with in a way that makes tracing and debugging more transparent.
Scoped values are an alternative to thread-local variables. Using values rather than variables can improve performance and make code simpler to understand.
- Unnamed Classes and Instance Main Methods (JEP 445): This feature is aimed at making it easier to start with the Java language. To write even the most straightforward “Hello World” application in Java requires a relatively large amount of code and an understanding of fundamental concepts. It is now possible to use just three lines of code:
void main() {
System.out.println(“Hello World”);
}
Along with further updates to the incubator modules for the Vector API (JEP 448) and the Foreign Function and Memory API (JEP 442), JDK 21 is one of the most feature-rich releases since JDK 9.
Azul will provide more detailed coverage of these features through focused blog posts, webinars and presentations at developer conferences. Be sure to check the rest of our site for links to these.
If you’re ready to give JDK 21 a try, you can find our free and commercially supported Zulu builds of OpenJDK available for download now. Why not give it a try?
Download JDK 21
Find your version, operating system, and architecture.