Contents

Exploring JDK 21 New Feature: Virtual Threads

JDK 21, the latest LTS release of OpenJDK, introduces an exciting new feature called Virtual Threads. In this blog post, we will delve into the details of this feature and explore how it enhances the scalability and performance of concurrent applications.

Overview

  • From Release Information, JDK 21 will be a long-term support (LTS) release
  • And in 2023/09/19 JDK 21 General Availability
  • JDK release step: Alpha –> Beta –> Release Candidate –> General Availability –> Release –> Stable
  • From Oracle Java SE Support Roadmap, Basically one LTS is released after every four non-LTS releases, so that JDK 8, JDK 11 and JDK 21 are important in release history.

Release information

/20231104_jdk21-new-feature/image-20231101152136597.png

Oracle Java SE Support Roadmap

ReleaseGA DatePremier Support UntilExtended Support UntilSustaining Support
8 (LTS)**March 2014March 2022December 2030*****Indefinite
9 - 10 (non-LTS)September 2017 - March 2018March 2018 - September 2018Not AvailableIndefinite
11 (LTS)September 2018September 2023January 2032Indefinite
12 - 16 (non-LTS)March 2019 - March 2021September 2019 - September 2021Not AvailableIndefinite
17 (LTS)September 2021September 2026****September 2029****Indefinite
18 (non-LTS)March 2022September 2022Not AvailableIndefinite
19 (non-LTS)September 2022March 2023Not AvailableIndefinite
20 (non-LTS)March 2023September 2023Not AvailableIndefinite
21 (LTS)***September 2023September 2028September 2031Indefinite
22 (non-LTS)***March 2024September 2024Not AvailableIndefinite
23 (non-LTS)***September 2024March 2025Not AvailableIndefinite
24 (non-LTS)***March 2025September 2025Not AvailableIndefinite
25 (LTS)***September 2025September 2033****Not AvailableIndefinite

Release step

/20231104_jdk21-new-feature/image-20231101153738703.png

New features

/20231104_jdk21-new-feature/image-20231101155956124.png

Virtual Threads

Overview

/20231104_jdk21-new-feature/0t_mVeMqHHrwso09p.png

Virtual Threads offers a more efficient and lightweight thread model.

Virtual threads in JDK 21 are implemented using a concept called "continuations." Continuations allow threads to be suspended and resumed without the need for full thread context switching, resulting in a more efficient and lightweight threading model.

Traditionally, each thread in Java corresponds to an operating system thread, which involves significant overhead in terms of memory and context switching. In contrast, virtual threads are not directly mapped to OS threads. Instead, they are managed by a lightweight scheduler within the JVM.

When a virtual thread encounters a blocking operation, such as waiting for I/O or synchronization, it can be suspended and its state is captured as a continuation. This continuation represents the thread’s execution context, including the call stack and local variables. The virtual thread is then freed up to execute other tasks.

Once the blocking operation completes, the virtual thread can be resumed from its captured continuation, allowing it to continue execution from the point where it was suspended. The resumption process does not involve the expensive thread context switching that occurs with traditional threads.

By leveraging continuations and the lightweight scheduler, JDK 21’s virtual threads provide a more efficient and scalable threading model, enabling developers to achieve higher levels of concurrency and responsiveness in their applications.

Why virtual threads

  • The costs of threads include creation, destruction, frequent switching, and concurrency overhead.
  • The costs of creation and destruction can be solved by Thread pool.
  • Frequent switching and concurrency overhead can be solved by Virtual Threads
    • Cause Virtual Threads in the same Thread, so there is no costs of switching
    • Also, virtual threads greatly increase the number of ’threads'

/20231104_jdk21-new-feature/0t_mVeMqHHrwso09p.png

Code

  • The code for virtual threads is basically the same as for thread pools

    1
    2
    3
    4
    5
    6
    7
    8
    
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()){
        IntStream.range(0, 10_000).forEach(i -> {
            executor.submit(() -> {
                Thread.sleep(Duration.ofSeconds(1));
                return i;
            });
        });
    } // executor.close() is called implicitly, and waits
    

Other Features

Sequedced Collections

Java’s collections framework lacks a sequence of elements representing a defined order. There is also a lack of a unified way to operate collections

Example:

First elementLast element
Listlist.get(0)list.get(list.size() - 1)
Dequedeque.getFirst()deque.getLast()
SortedSetsortedSet.first()sortedSet.last()
LinkedHashSetlinkedHashSet.iterator().next()//missing

New Sequedced Collections:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
interface SequencedCollenction<E> extends Collection<E> {
    // new method
    SequencedCollection<E> reversed();
    // methods promoted from Deque
    void addFirst(E);
    void addLast(E);
    E geFirst();
    E getLast();
    E removeFirst();
    E removeLast();
}

/20231104_jdk21-new-feature/image-20231102162648946.png

Deprecate the 32-bit x86 port for Removal

  • 64 bits are simulated by writing high 12 bits once and low 32 bits once

Prepare to disallow the Dynamic Loading of Agents

  • Warning when the agent is dynamically loaded into a running JVM. These warnings are intended to prepare users for a future release that does not allow dynamic loading of the agent by default in order to improve integrity by default.

  • Dynamic loading agents allow developers to modify and monitor the behavior of Java applications at run time. While this is useful for debugging and performance analysis, there are also potential security risks. Malicious code may take advantage of dynamically loaded agents to perform malicious operations, such as fetching sensitive information, calculating data, and so on. Therefore, in order to strengthen the security of Java applications, it is necessary to limit the use of dynamically loaded agents

Generational ZGC

/20231104_jdk21-new-feature/image-20231102211953084.png

Pattern Matching for switch

/20231104_jdk21-new-feature/image-20231102212225874.png

Pattern Matching comparing:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Before Java 21
static String formatter(Object obj){
    String formatted = "unknown";
    if(obj instanceof Integer i){
        formatted = String.format("int %d", i);
    } else if (obj instanceof Long l){
        formatted = String.format("long %d", l);
    } else if (obj instanceof Double d){
        formatted = String.format("double %f", d);
    } else if (obj instanceof String s){
        formatted = String.format("String %s", s);
    }
    return formatted;
}

// In Java 21
static String formatterPatternSwitch(Object obj){
    return switch(obj){
            case Integer i -> String.format("int %d", i);
            case Long l    -> String.format("long %d", l);
            case Double d  -> String.format("double %f", d);
            case String s  -> String.format("String %s", s);
            default -> obj.toString();
    };
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Before Java 21
static void test(String s){
    if(s == null){
        println("oops");
        reutrn;
    }
    switch(s){
            case "Foo", "Bar" -> println("great");
            default           -> println("OK");
    }
}

// In Java 21
static void test(String s){
    switch(s){
            case null         -> println("oops");
            case "Foo", "Bar" -> println("great");
            default           -> println("OK");
    }
}

Record Patterns

1
2
3
4
5
6
// In Java 21
static void printsum(Object obj){
    if(obj instanceof point(int x, int y)){
        println(x + y);
    }
}

KEM

Key Encapsulation Mechanism API

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Receiver side
var kpg = KeyPairGenerator.getInstance("x2551g");
var kp = kpg.generateKeyPair();
// sender side
var kem1 = KEM.getInstance("DHKEM");
var sender = kem1.newEncapsulator(kp.getpublic());
var encapsulated = sender.encapsulate();
var k1 = encapsulated.key();
// Receiver side
var kem2 = KEM.getInstanceC"DHKEM");
var receiver = kem2.newDecapsulator(kp.getprivate());
var k2 = receiver.decapsulate(encapsulated.encapsulation());
assert Arrays.equals(kl.getEncoded(), k2.getEncoded());