Java语言特性系列
- Java5的新特性
- Java6的新特性
- Java7的新特性
- Java8的新特性
- Java9的新特性
- Java10的新特性
- Java11的新特性
- Java12的新特性
- Java13的新特性
- Java14的新特性
- Java15的新特性
- Java16的新特性
- Java17的新特性
- Java18的新特性
- Java19的新特性
- Java20的新特性
- Java21的新特性
- Java22的新特性
序
本文主要讲述一下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.jar App
移除项
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 JNI_VERSION_19 (JDK-8286176)
GetVersion这个jni方法返回JNI_VERSION_19
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命令行选项指定的值不同;它可能四舍五入为系统所需的页面大小的倍数。
小结
Java19主要有如下几个特性
- 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)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。