JDK21來了!附重要更新說明

DaFanJoy發表於2023-09-18

JDK21 計劃23年9月19日正式釋出,雖然一直以來都是“版本隨便出,換 8 算我輸”,但這麼多年這麼多版本的折騰,如果說之前的 LTS版本JDK17你還覺得不香,那 JDK21還是有必要關注一下,因為會有一批重要更新發布到生產環境中,特別是千呼萬喚的虛擬執行緒,雖然說這東西我感覺不需要的用不到,需要的早都轉go了,哈哈,但作為近幾年JDK一個“重大”的更新,在實際開發應用中還是是有很大價值的。所以這篇文章主要提取了這次更新中個人感覺比較有價值的幾點做個基本的介紹,想要嚐鮮的同學可以看下。

Visual Threads (虛擬執行緒) -JEP 444

先看下官方對虛擬執行緒(Visual Threads)描述:

Today, every instance of java.lang.Thread in the JDK is a platform thread. A platform thread runs Java code on an underlying OS thread and captures the OS thread for the code's entire lifetime. The number of platform threads is limited to the number of OS threads.

virtual thread is an instance of java.lang.Thread that runs Java code on an underlying OS thread but does not capture the OS thread for the code's entire lifetime. This means that many virtual threads can run their Java code on the same OS thread, effectively sharing it. While a platform thread monopolizes a precious OS thread, a virtual thread does not. The number of virtual threads can be much larger than the number of OS threads.

Virtual threads are a lightweight implementation of threads that is provided by the JDK rather than the OS. They are a form of user-mode threads, which have been successful in other multithreaded languages (e.g., goroutines in Go and processes in Erlang). User-mode threads even featured as so-called "green threads" in early versions of Java, when OS threads were not yet mature and widespread. However, Java's green threads all shared one OS thread (M:1 scheduling) and were eventually outperformed by platform threads, implemented as wrappers for OS threads (1:1 scheduling). Virtual threads employ M:N scheduling, where a large number (M) of virtual threads is scheduled to run on a smaller number (N) of OS threads.

總結下就是之前java中的執行緒是“platform thread”即平臺執行緒,它由作業系統執行緒為基礎,按照1:1的模式排程,這導致執行緒的建立與執行都是很耗資源的,同時數量也受系統的約束;但新的虛擬執行緒則是由JDK提供,你可以把它看作是在平臺執行緒基礎上建立的“一批”執行緒,它們有效地共享所屬的平臺執行緒也就是作業系統執行緒的資源,從而提升系統利用率,並不受數量限制。

目標描述:

1、Enable server applications written in the simple thread-per-request style to scale with near-optimal hardware utilization.

可以每個請求開啟一個虛擬執行緒,實現簡單直接的同時可以最大程度的提升硬體利用率;

2、Enable existing code that uses the java.lang.Thread API to adopt virtual threads with minimal change.

之前的多執行緒實現程式碼可以在較小的改動下完成向虛擬執行緒的遷移;

3、Enable easy troubleshooting, debugging, and profiling of virtual threads with existing JDK tools.

使用現有JDK工具可以完成虛擬執行緒的程式碼除錯、分析與問題定位;

說白了就是現在我們不用怎麼改程式碼就可以建立一個輕量級的虛擬執行緒,實現簡單同時還能夠充分發揮硬體效能。

一個簡單的程式碼示例

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 10000).forEach(i -> {
        executor.submit(() -> {
            Thread.sleep(Duration.ofSeconds(1));
            return i;
        });
    });
}  // executor.close() is called implicitly, and waits

SequencedCollection Interface(順序集合 介面)

兄弟們,作為一個天天CRUD,CPU跑不滿20%的程式設計師, 相比上面的虛擬執行緒,這次關於集合類介面的更新我感覺更實在一些

JDK21中我們常用的Set、List、Deque與Map集合類分別繼承實現了SequencedCollection、 SequencedMap 介面,為我們執行一些順序性操作比如獲取頭尾值提供了各類介面方法

繼承關係如下圖所示:

介面定義如下

interface SequencedCollection<E> extends Collection<E> {
    // new method
    SequencedCollection<E> reversed();
    // methods promoted from Deque
    void addFirst(E);
    void addLast(E);
    E getFirst();
    E getLast();
    E removeFirst();
    E removeLast();
}
interface SequencedSet<E> extends Set<E>, SequencedCollection<E> {
    SequencedSet<E> reversed();    // covariant override
}
interface SequencedMap<K,V> extends Map<K,V> {
    // new methods
    SequencedMap<K,V> reversed();
    SequencedSet<K> sequencedKeySet();
    SequencedCollection<V> sequencedValues();
    SequencedSet<Entry<K,V>> sequencedEntrySet();
    V putFirst(K, V);
    V putLast(K, V);
    // methods promoted from NavigableMap
    Entry<K, V> firstEntry();
    Entry<K, V> lastEntry();
    Entry<K, V> pollFirstEntry();
    Entry<K, V> pollLastEntry();
}

Record Patterns (記錄模式)-JEP 440

這個更新主要簡化了型別判斷與賦值的使用,型別判斷後無需顯式強制轉換且如果模式匹配,變數被初始化為要匹配的模板值, 這個說起來比較拗口,結合程式碼大家理解下,我感覺還是挺有用的,這裡我把JDK8 JDK17 JDK21 的實現進行一個對比,大家就明白了。

// As of Java 8
record Point(int x, int y) {}

static void printSum(Object obj) {
    if (obj instanceof Point) {
        Point p = (Point) obj;
        int x = p.x();
        int y = p.y();
        System.out.println(x+y);
    }
}
// As of Java 16
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);
    }
}
// As of Java 21
static void printSum(Object obj) {
    if (obj instanceof Point(int x, int y)) {
        System.out.println(x+y);
    }
}

Pattern Matching for switch (switch模式匹配) – JEP 441

switch 的模式匹配可以與Record Patterns結合使用 允許在任何物件上制定 switch 語句和表示式。看一下程式碼例子:

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);
        case Position(int x, int y)   -> String.format("String %s,String %s", x,y);
        default        -> obj.toString();
    };
}

同時當編譯器判斷所有分支都已涵蓋時,switch不再需要分支default,如下面的程式碼

void flyJava21(Direction direction) { 
    switch (direction) {
       case CompassDirection.NORTH -> System.out.println("Flying north"); 
       case CompassDirection.SOUTH -> System.out.println("Flying south");
       case CompassDirection.EAST -> System.out.println("Flying east");
       case CompassDirection.WEST -> System.out.println("Flying west"); 
       case VerticalDirection.UP -> System.out.println("Gaining altitude"); 
       case VerticalDirection.DOWN -> System.out.println("Losing altitude"); 
    } 
}

Generational ZGC(分代式 ZGC) -JEP 439

主要是增加了對分代的支援,提高垃圾回收的效能,看下整體描述

To ensure a smooth succession, we will initially make Generational ZGC available alongside non-generational ZGC. The -XX:+UseZGC command-line option will select non-generational ZGC; to select Generational ZGC, add the -XX:+ZGenerational option:

使用命令列選項 -XX:+UseZGC 將選擇非分代式 ZGC;要選擇分代式 ZGC,需要新增 -XX:+ZGenerational 選項。

$ java -XX:+UseZGC -XX:+ZGenerational ...

In a future release we intend to make Generational ZGC the default, at which point -XX:-ZGenerational will select non-generational ZGC. In an even later release we intend to remove non-generational ZGC, at which point the ZGenerational option will become obsolete.

總結就是當前版本中如果想使用ZGC,命令列選項增加 -XX:+UseZGC,但預設是非分代式GC;要使用分代式ZGC ,需要改為 $ java -XX:+UseZGC -XX:+ZGenerational ,而在後續的版本中會預設改為分代GC。

以上就是JDK21版本中我感覺一些有價值的更新,如果大家希望能夠更進一步的瞭解還是要去官網https://openjdk.org/projects/jdk/21/ 自行檢視,並在釋出之後在使用中實際驗證。

之所以有這次總結,主要還是有感於java近年來受到其他各類語言的衝擊,頗有垂垂老矣的感覺,讓我這個C#出身的javaer又想起了一段不好的回憶,哈哈,所以看看這次的更新是否能給點力, 當前java語言的下行趨勢也可以說是國內IT行業興衰起伏的一個側面寫照,當然作為一個提供生產力的程式語言,圍繞java建立起的整個完整、穩定、強大的生態仍然會在它適合的領域起到重要的作用,這是一時半會無法改變的,但你指望靠它幹到退休估計連收了學費的培訓機構也不敢這樣說,程式設計師的職業壽命從來不是某種程式語言決定的。最後我還是想說入行的菜鳥才糾結於語言優劣,成熟老手知道這早有定論,讓我們大聲喊出:   

                                     "PHP是最好的語言"

這不是玩梗,因為從某種角度上講: 

                                 "生產力才是決定語言生死的關鍵"
  •  
  •  
  •  參考資料:https://openjdk.org/projects/jdk/21/
  •  
  •  

   關注微信公眾號,檢視更多技術文章。

相關文章