Blog post

The new JDK LTS is out! Long live JDK 21!

Jonathan Vila

Jonathan Vila

Developer Advocate - Java

5 min read

  • Java
Java JDK 21 LTS features

The new Long Term Support JDK version was released as GA on September 19th. It’s been 2 years since the previous LTS version, JDK 17, was released.


With the current approach for LTS versions, a new one will be released every 2 years, with a 6-month cadence for regular versions.


But, at this point, we could be wondering…

What’s JDK 21 LTS really? 

Well, it’s “a company's offer to provide services and guarantees for their certified Java implementation, that may or may not be built from an OpenJDK update fork.” quoting Nicolai Parlog from Oracle. 


So we rely on the vendor who is providing the Java binary that we use and its particular definition of LTS. Different vendors have different roadmaps and support ranges. LTS versions are usually supported for 8 years, for example, Oracle and Azul are committed until 2031. There are alternatives,  for example, Azul has MTS (Medium Term Support) versions.

And why should I use an JDK 21 LTS?

During the LTS support term bug fixes and vulnerabilities will be ported to that version. For other JDK versions, this process only lasts 6 months.


So, if we use non-LTS versions in production, to be safe we need to update the JDK version every 6 months, running the risk of having deprecated or removed functionalities. LTS is definitely the version to use in production.

And what does this new JDK 21 LTS version bring?

Lots of bugs and CVEs are fixed, but also great new production-ready features. I’ll highlight some of them:

  • JEP413 - Code snippets in Java API documentation
  • JEP431 - Sequenced Collections
  • JEP440 - Record Patterns
  • JEP441 - Pattern Matching for Switch
  • JEP444 - Virtual Threads
  • JEP451 - Prepare to Disallow the Dynamic Loading of agents

Code snippets in Java API documentation

This new feature allows adding code inside the documentation that will be included in the Java Docs, both from inline code or from external files. It accepts markup tags like @highlight or @replace. 

/**
 * The following code shows how to use {@code Optional.isPresent}:
 * {@snippet :
 * if (v.isPresent()) { // @highlight substring="isPresent"
 *     System.out.println("v: " + v.get());
 * }
 * }
 * Where v != null
 */

Output

java public boolean snippet

Sequenced Collections in Java

Collections in Java have always lacked an ordered approach. But now new interfaces `SequencedCollection, SequencedSet, SequencedMap` are filling this gap by adding consistent methods across the collections: addFirst, addLast, getFirst, getLast, removeFirst, removeLast


Another problem with collections has been trying to get the elements in a reversed order, but with the new reversed method we have a consistent and effective way of getting them.


This has also been retrofitted to current interfaces: List, SortedSet, LinkedHashSet, Deque, SortedMap, and LinkedHashMap.


Before Java 21:

var first = list.iterator().next(); 
var last = list.get(arrayList.size() - 1);

With the new sequenced collections, we can do the same thing using simpler methods:

var first = list.getFirst();
var last = list.getLast();

Record patterns

We can use a type pattern to test whether a value is an instance of a record class and extract the component values. For example, with a record Point, you can extract the x and y values.


Before Java 21

record Point(int x, int y) {}

static void printSum(Object obj) {
    if (obj instanceof Point p) {
        int x = p.x();
        int y = p.y();
        System.out.println(x+y);
    }
}

With Java 21

record Point(int x, int y) {}

static void printSum(Object obj) {
  if (obj instanceof Point(int x, int y)) { 
    System.out.println(x+y); 
  }
}

Pattern matching for Switch

The power of pattern matching is expanded to Switch statements to reduce boilerplate code and improve readability.


Before JDK 21

record Point(int x, int y) {}

public void print(Object o) {
  switch (o) {
    case Point p -> System.out.printf("position: %d/%d%n", p.x(), p.y());
    case String s -> System.out.printf("string: %s%n", s);
    default       -> System.out.printf("something else: %s%n", o);
  }
}

In Java 21, we can write a similar expression with a record pattern as follows:

public void print(Object o) {
  switch (o) {
    case Point(int x, int y) -> System.out.printf("position: %d/%d%n", x, y);
    case String s            -> System.out.printf("string: %s%n", s);
    default                  -> System.out.printf("something else: %s%n", o);
  }
}

Virtual Threads

This is a great feature for JDK 21. Until now every thread that was created had a direct link with a platform or OS thread. Considering the limited availability of those threads it became hard to handle a high magnitude of concurrency.


Virtual threads are a concept that allows having millions of “threads” mapping several virtual threads to one platform or OS thread. With virtual threads, the blocking calls to I/O will be suspended and the thread will be used for another process, just expecting the call to finish eventually. 


Basically, the physical threads are shared among virtual threads, allowing hardware utilization to be close to optimal with a high level of concurrency. As a result, it will allow higher throughput, while the application remains harmonious with the multithreaded design of the Java Platform and its tooling. 

Runnable runnable = () -> System.out.println("Inside Runnable"); 
Thread.startVirtualThread(runnable);

Disallow Dynamic loading of Agents

Agents have been used mainly to allow tools and profiles to instrument classes, and we can find several of them to get observability for the JVM. Basically, agents are components that can alter the code of an application while it is running.


But, it can also be a back door used by agents to alter the normal behavior of an application, without even asking for permission.


In JDK 21, the dynamic loading of agents is allowed but the JVM issues a warning when it occurs.
In order to allow tools to dynamically load agents without showing warnings, the -XX:+EnableDynamicAgentLoading option must be added on the command line. And this will be the only way in the future to allow Agents.

JDK 21 LTS Conclusion

This new JDK 21 will definitely introduce new features that allow the code to be cleaner more secure and efficient, especially considering concurrency. 


With code snippets, we can improve the readability of code examples in the Java API definition. Pattern matching in records and switches will improve consistency by using conventions and making the code more identifiable. Sequenced collections will allow us to have clearer and more intentional code with consistent methods across all collections to obtain the first and last elements and an efficient way of reversing the elements. Virtual threads will boost our application's asynchronous performance making it more efficient. And finally disallowing dynamic agent loading will make the application more secure making it explicit to load agents.


It’s important to consider moving to the latest/closest LTS, even if its features don't excite you in order to have more stable, secure, and efficient software. Upgrading to the JDK 21 LTS for your production code will lock in support for the next 8 years.


Get new blogs delivered directly to your inbox!

Stay up-to-date with the latest Sonar content. Subscribe now to receive the latest blog articles. 

By submitting this form, you agree to the storing and processing of your personal data as described in the Privacy Policy and Cookie Policy. You can withdraw your consent by unsubscribing at any time.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

  • Legal documentation
  • Trust center
  • Follow SonarSource on Twitter
  • Follow SonarSource on Linkedin

© 2008-2024 SonarSource SA. All rights reserved. SONAR, SONARSOURCE, SONARQUBE, and CLEAN AS YOU CODE are trademarks of SonarSource SA.