天天看點

Java19的新特性

作者:碼匠亂炖

Java語言特性系列

Java5的新特性Java6的新特性Java7的新特性Java8的新特性Java9的新特性Java10的新特性Java11的新特性Java12的新特性Java13的新特性Java14的新特性Java15的新特性Java16的新特性Java17的新特性Java18的新特性Java19的新特性Java20的新特性

本文主要講述一下Java19的新特性

版本号

java -version
openjdk version "19" 2022-09-20
OpenJDK Runtime Environment (build 19+36-2238)
OpenJDK 64-Bit Server VM (build 19+36-2238, mixed mode, sharing)           
從version資訊可以看出是build 19+36

特性清單

JEP 405: Record Patterns (Preview)

instanceof的模式比對在JDK14作為preview,在JDK15作為第二輪的preview,在JDK16JEP 394: Pattern Matching for instanceof轉正 switch模式比對在JDK17的JEP 406: Pattern Matching for switch (Preview)引入作為preview版本,在JDK18的JEP 420: Pattern Matching for switch (Second Preview)作為第二輪的preview

針對record類型,引入instance of可以這麼寫

record Point(int x, int y) {}

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

現在可以這麼寫

record Point(int x, int y) {}

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

比較複雜的例子:

record Point(int x, int y) {}
enum Color { RED, GREEN, BLUE }
record ColoredPoint(Point p, Color c) {}
record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {}

Rectangle r = new Rectangle(new ColoredPoint(new Point(x1, y1), c1), 
                            new ColoredPoint(new Point(x2, y2), c2));

static void printXCoordOfUpperLeftPointWithPatterns(Rectangle r) {
    if (r instanceof Rectangle(ColoredPoint(Point(var x, var y), var c),
                               var lr)) {
        System.out.println("Upper-left corner: " + x);
    }
}           

如果是泛型record的話:

record Box<T>(T t) {}

static void test1(Box<Object> bo) {
    if (bo instanceof Box<Object>(String s)) {
        System.out.println("String " + s);
    }
}
static void test2(Box<Object> bo) {
    if (bo instanceof Box<String>(var s)) {
        System.out.println("String " + s);
    }
}           

JEP 422: Linux/RISC-V Port

RISC-V是一個基于精簡指令集(RISC)原則的開源指令集架構(ISA),這個JEP則移植JDK到RISC-V上

JEP 424: Foreign Function & Memory API (Preview)

Foreign Function & Memory (FFM) API包含了兩個incubating API JDK14的JEP 370: Foreign-Memory Access API (Incubator)引入了Foreign-Memory Access API作為incubator JDK15的JEP 383: Foreign-Memory Access API (Second Incubator)Foreign-Memory Access API作為第二輪incubator JDK16的JEP 393: Foreign-Memory Access API (Third Incubator)作為第三輪,它引入了Foreign Linker API (JEP 389) FFM API在JDK 17的JEP 412: Foreign Function & Memory API (Incubator)作為incubator引入 FFM API在JDK 18的JEP 419: Foreign Function & Memory API (Second Incubator)作為第二輪incubator JDK19的這個JEP則将FFM API作為preview API

使用示例

// 1. Find foreign function on the C library path
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
MethodHandle radixSort = linker.downcallHandle(
                             stdlib.lookup("radixsort"), ...);
// 2. Allocate on-heap memory to store four strings
String[] javaStrings   = { "mouse", "cat", "dog", "car" };
// 3. Allocate off-heap memory to store four pointers
SegmentAllocator allocator = SegmentAllocator.implicitAllocator();
MemorySegment offHeap  = allocator.allocateArray(ValueLayout.ADDRESS, javaStrings.length);
// 4. Copy the strings from on-heap to off-heap
for (int i = 0; i < javaStrings.length; i++) {
    // Allocate a string off-heap, then store a pointer to it
    MemorySegment cString = allocator.allocateUtf8String(javaStrings[i]);
    offHeap.setAtIndex(ValueLayout.ADDRESS, i, cString);
}
// 5. Sort the off-heap data by calling the foreign function
radixSort.invoke(offHeap, javaStrings.length, MemoryAddress.NULL, '\0');
// 6. Copy the (reordered) strings from off-heap to on-heap
for (int i = 0; i < javaStrings.length; i++) {
    MemoryAddress cStringPtr = offHeap.getAtIndex(ValueLayout.ADDRESS, i);
    javaStrings[i] = cStringPtr.getUtf8String(0);
}
assert Arrays.equals(javaStrings, new String[] {"car", "cat", "dog", "mouse"});  // true           

JEP 425: Virtual Threads (Preview)

虛拟線程應該是JDK19最重磅的特性,首次內建進來即為preview的API 虛拟線程是由JDK提供的使用者态線程,類似golang的goroutine,erlang的processes 虛拟線程采用的是M:N的排程模式,即M數量的虛拟線程運作在N個Thread上,使用的是ForkJoinPool以FIFO模式來進行排程,N預設是為Runtime.availableProcessors(),可以通過 jdk.virtualThreadScheduler.parallelism來修改

使用示例

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           
如上使用了少數幾個OS線程來運作10000個虛拟線程 虛拟線程在超過上千個非CPU密集并發任務場景可以顯著提升系統的吞吐率
void handle(Request request, Response response) {
    var url1 = ...
    var url2 = ...

    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        var future1 = executor.submit(() -> fetchURL(url1));
        var future2 = executor.submit(() -> fetchURL(url2));
        response.send(future1.get() + future2.get());
    } catch (ExecutionException | InterruptedException e) {
        response.fail(e);
    }
}

String fetchURL(URL url) throws IOException {
    try (var in = url.openStream()) {
        return new String(in.readAllBytes(), StandardCharsets.UTF_8);
    }
}           

像這種場景雖然是block的代碼,但是因為引入的是虛拟線程,系統可以很好地伸縮;當虛拟線程block在IO或者其他操作( BlockingQueue.take())時,虛拟線程會從Thread unmount,當操作完成才重新mount上繼續執行。不過有些操作不會unmount虛拟線程,會一同thread和底層的OS線程一起block住(比如進入synchronized代碼塊/方法,比如執行一個native方法或者foreign function)。 虛拟線程開銷不大,因而不需要使用池化技術 使用 jcmd<pid>Thread.dump_to_file-format=json<file>可以以json格式來dump虛拟線程,執行個體如下

Thread.Builder, Thread.ofVirtual(), Thread.ofPlatform() 可以用來建立虛拟線程或者是平台線程,比如

Thread thread = Thread.ofVirtual().name("duke").unstarted(runnable);           
Thread.startVirtualThread(Runnable)等同于建立和啟動虛拟線程 Thread.threadId() 作為final方法會傳回線程辨別,而非final的Thread.getId()則被廢棄 Thread.getAllStackTraces()現在傳回的是平台線程而非所有線程

JEP 426:Vector API (Fourth Incubator)

JDK16引入了JEP 338: Vector API (Incubator)提供了jdk.incubator.vector來用于矢量計算 JDK17進行改進并作為第二輪的incubatorJEP 414: Vector API (Second Incubator) JDK18的JEP 417: Vector API (Third Incubator)進行改進并作為第三輪的incubator,而JDK19則作為第四輪的incubator

JEP 427: Pattern Matching for switch (Third Preview)

instanceof的模式比對在JDK14作為preview,在JDK15作為第二輪的preview,在JDK16轉正 JDK17引入JEP 406: Pattern Matching for switch (Preview) JDK18的JEP 420: Pattern Matching for switch (Second Preview)則作為第二輪的preview,JDK19作為第三輪preview

JEP 428: Structured Concurrency (Incubator)

結構化并發也是JDK19的一個重要特性。JDK5引入的ExecutorService可以用于并行處理任務,比如
Response handle() throws ExecutionException, InterruptedException {
    Future<String>  user  = esvc.submit(() -> findUser());
    Future<Integer> order = esvc.submit(() -> fetchOrder());
    String theUser  = user.get();   // Join findUser
    int    theOrder = order.get();  // Join fetchOrder
    return new Response(theUser, theOrder);
}           
但是當findUser抛出異常時,fetchOrder還是會在自己的線程繼續運作,或者findUser需要運作很長時間,而當fetchOrder異常時,整個handle方法還是需要浪費時間等待findUser執行完,它阻塞在user.get()。為了更好地處理這種在異常場景下取消其他子任務,引入結構化并發來解決此問題,其主要是StructuredTaskScope這個類,它可以fork子任務,然後一起join或者一起cancel。
Response handle() throws ExecutionException, InterruptedException {
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        Future<String>  user  = scope.fork(() -> findUser());
        Future<Integer> order = scope.fork(() -> fetchOrder());

        scope.join();           // Join both forks
        scope.throwIfFailed();  // ... and propagate errors

        // Here, both forks have succeeded, so compose their results
        return new Response(user.resultNow(), order.resultNow());
    }
}           
如果其中一個子任務失敗了,則會取消另外一個在運作的任務。在scope.join()之後,可以使用resultNow()或者exceptionNow()來擷取結果

細項解讀

上面列出的是大方面的特性,除此之外還有一些api的更新及廢棄,主要見JDK 19 Release Notes,這裡舉幾個例子。

添加項

  • System Properties for System.out and System.err (JDK-8283620)
新增stdout.encoding這兩個系統屬性stderr.encoding
  • Support Unicode 14.0 (JDK-8268081)
新增支援unicode 14.0版本
  • Additional Date-Time Formats (JDK-8176706)
以前java.time.format.DateTimeFormatter/DateTimeFormatterBuilder隻支援FormatStyle.FULL/LONG/MEDIUM/SHORT這四種,現在可以自定義,比如 DateTimeFormatter.ofLocalizedPattern("yMMM")
  • Automatic Generation of the CDS Archive (JDK-8261455)
新增 -XX:+AutoCreateSharedArchive參數可以自動建立CDS archive,比如 java-XX:+AutoCreateSharedArchive-XX:SharedArchiveFile=app.jsa-cp app.jarApp

移除項

  • Remove Finalizer Implementation in SSLSocketImpl (JDK-8212136)
移除SSLSocket的finalizer實作
  • Removal of Diagnostic Flag GCParallelVerificationEnabled (JDK-8286304)
移除GCParallelVerificationEnabled參數

廢棄項

完整清單見Java SE 19 deprecated-list

  • java.lang.ThreadGroup Is degraded (JDK-8284161)
ThreadGroup不能再被顯示destroy了,它現在不再與其subgroup保持強引用
  • Deprecation of Locale Class Constructors (JDK-8282819)
Locale的構造器被廢棄了,可用Locale.of()這個工廠方法替代

已知問題

  • ForkJoinPool and ThreadPoolExecutor do not use Thread::start to Start Worker Threads (JDK-8284161)
ForkJoinPool及ThreadPoolExecutor在這個版本不再使用`Thread::start來啟動線程了,因而那些override無參start的工作線程可能會受影響,不過ForkJoinWorkerThread.onStart()不受影響
  • InflaterInputStream.read Throws EOFException (JDK-8292327)
InflaterInputStream在這個版本可能抛出EOFException
  • G1 Remembered set memory footprint regression after JDK-8286115 (JDK-8292654)
JDK-8286115這個變動了G1的RSet的ergonomic size,會造成本地記憶體使用增加,可以通過增加G1RemSetArrayOfCardsEntries值來緩解,比如
-XX:+UnlockExperimentalVMOptions -XX:G1RemSetArrayOfCardsEntries=128           

其他事項

  • JNI GetVersion Returns JNIVERSION19 (JDK-8286176)
GetVersion這個jni方法傳回JNIVERSION19
  • Double.toString(double) and Float.toString(float) may Return Slightly Different Results (JDK-4511638)
Double.toString(2e23), 現在會傳回"2.0E23", 而之前的版本會傳回"1.9999999999999998E23"
  • Make HttpURLConnection Default Keep Alive Timeout Configurable (JDK-8278067)
新增了http.keepAlive.time.server及http.keepAlive.time.proxy系統屬性可以用于修改預設的Keep Alive Timeout
  • JVM TI Changes to Support Virtual Threads (JDK-8284161)
The JVM Tool Interface (JVM TI)已經更新,現可支援虛拟線程
  • -Xss may be Rounded up to a Multiple of the System Page Size (JDK-8236569)
實際的java線程堆棧大小可能與-Xss指令行選項指定的值不同;它可能四舍五入為系統所需的頁面大小的倍數。

小結

Java18主要有如下幾個特性

  • JEP 405: Record Patterns (Preview)
  • JEP 422: Linux/RISC-V Port
  • JEP 424: Foreign Function & Memory API (Preview)
  • JEP 425: Virtual Threads (Preview)
  • JEP 426: Vector API (Fourth Incubator)
  • JEP 427: Pattern Matching for switch (Third Preview)
  • JEP 428: Structured Concurrency (Incubator)

doc

  • JDK 19 Features
  • JDK 19 Release Notes
  • Consolidated JDK 19 Release Notes
  • Java SE 19 deprecated-list
  • The Arrival of Java 19
  • JDK 19 G1/Parallel/Serial GC changes
  • Java 19 Delivers Features for Projects Loom, Panama and Amber
  • JDK 19 and JDK 20: What We Know So Far

繼續閱讀