Z次元
文章 笔记 日志
专题
专栏分类 文章归档
友链
友情链接 朋友圈
交流
留言 关于我
头像
系列文章
-
-
-
-
JDK新版特性(18-21)
系列文章
知识积累
最后更新:2025/2/16|创建时间:2025/2/7

文章主要介绍了JDK18-21新版本的主要特性

JDK18

switch 模式匹配(第二次预览)

JDK19

  • Record 模式匹配 (预览)

  • 虚拟线程 (预览)

  • Switch 模式匹配 (第三次预览)

JDK20

  • Record 模式匹配(第二次预览)

  • switch 的模式匹配(第四次预览)

  • 虚拟线程(第二次预览)

JDK21

虚拟线程

虚拟线程是 Java 21 中最为重要的特性。Java 从 Java 19 开始引入虚拟线程,在 Java 21 中就正式升级为正式特性。可见官方也把虚拟线程作为 Java 21 长久支持版本的吸引点。虚拟线程是轻量级的线程,可以在显著的减少代码编写的同时提高系统的吞吐量。

注意:虚拟线程可以提高系统的吞吐量,不能提高运行速度,也不适用于 CPU 计算密集型任务

引入虚拟线程原因

一直以来,在 Java 并发编程中,Thread 都是十分重要的一部分,Thread 是 Java 中的并发单元,每个 Thread 线程都提供了一个堆栈来存储局部变量和方法调用,以及线程上下文等相关信息。

但问题是线程和进程一样,都是一项昂贵的资源,JDK 将 Thread 线程实现为操作系统线程的包装器,成本很高,而且数量有限。因此我们会使用线程池来管理线程,同时限制线程的数量。比如常用的 Tomcat 会为每次请求单独使用一个线程进行请求处理,同时限制处理请求的线程数量以防止线程过多而崩溃;这很有可能在 CPU 或网络连接没有耗尽之前,线程数量已经耗尽,从而限制了 web 服务的吞吐量。

可能有些同学要说了,那么可以放弃请求和线程一一对应的方式,使用异步编程来解决这个问题。把请求处理分段,在组合成顺序管道,通过一套 API 进行管理,这样就可以使用有限的线程来处理超过线程数量的请求。这当然也是可以的,但是随之而来的问题是:

  • 需要额外的学习异步编程。

  • 代码复杂度增加,等于放弃了语言的基本顺序组合运算。

  • 堆栈上下文信息都变得难以追踪。

  • Debug 困难。

  • 和 Java 平台本身的编程风格有冲突,Java 并发单元是 Thread,而这时是异步管道。

而事实上,以上面的请求开启一个线程处理为例,因为 DB 查询速度过慢,请求量过大,可能导致我们的线程数量已经使用殆尽,新的请求将被阻塞,但是机器的性能尚有剩余剩余,性能浪费。

那么对于这种需要提高吞吐量的场景,使用虚拟线程将会大大改善这种情况。

虚拟线程的使用

这里我们不去介绍虚拟线程的实现原理,对开发者来说虚拟线程在使用体验上和 Thread 几乎没有区别,与之前的 API 互相兼容,但是相比之下虚拟线程资源占用非常少,虚拟线程是一种即用即启动的线程,不应该被池化存储。

创建提交执行虚拟线程

下面是一个示例,创建 1 万个线程,然后都休眠 1 秒钟结束线程,如果使用传统的 Thread 线程,可能会因为线程数量不够而直接异常。如果是线程池的方式,会基于线程池的线程数并发,那么剩余线程只能等待;但是使用虚拟线程的方式,可以瞬间完成。

import java.time.Duration;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

public class VirtualThreadTest {
    public static void main(String[] args) throws InterruptedException {
        // 创建并提交执行虚拟线程
        long start = System.currentTimeMillis();
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            IntStream.range(0, 10_000).forEach(i -> {
                executor.submit(() -> {
                    Thread.sleep(Duration.ofSeconds(1));
                    return i;
                });
            });
        }
        System.out.println("time:" + (System.currentTimeMillis() - start) + "ms");
    }
}

执行后发现仅需 1.052 秒就执行完毕。

$ java ep444VirtualThread.java
time:1052ms

设置虚拟线程名称

Thread thread1 = Thread.ofVirtual().name("v-thread").unstarted(() -> {
    String threadName = Thread.currentThread().getName();
    System.out.println(String.format("[%s] Hello Virtual Thread", threadName));
});
thread1.start();

输出:[v-thread] Hello Virtual Thread

启动为虚拟线程

Thread thread2 = new Thread(() -> {
    String threadName = Thread.currentThread().getName();
    System.out.println(String.format("[%s] Hello Virtual Thread 2", threadName));
});
Thread.startVirtualThread(thread2);

判断是否是虚拟线程

最后,可以使用 isVirtual 方法判断一个线程对象是否是虚拟线程。

Thread thread1 = Thread.ofVirtual().name("v-thread").unstarted(() -> {
    String threadName = Thread.currentThread().getName();
    System.out.println(String.format("[%s] Hello Virtual Thread", threadName));
});
// 判断是否是虚拟线程
System.out.println(thread1.isVirtual());

有序集合

在 Java 中,集合类库非常重要且使用频率非常高,各种各样的集合类型有些对插入元素有序有些无序。对元素插入有序的集合如各种 List,Deque,以及 Linked 种类的 set 和 map 等。但是这些有序集合在 JDK 中通过类库的设计并没有体现出来。甚至使用方式也不相同。

下面是它们使用上的一些区别。

集合

获取第一个元素

获取最后一个元素

List

list.get(0)

list.get(list.size() - 1)

Deque

deque.getFirst()

deque.getLast()

SortedSet

sortedSet.first()

sortedSet.last()

LinkedHashSet

linkedHashSet.iterator().next()

下面是在 JDK 21 之前,对几个有序集合的操作示例,可见不管是获取第一个元素、获取最后一个元素、还是逆序遍历等,操作方式都不相同,这样很容易混乱。

JDK 21 之前有序集合操作

示例:JDK 21 之前有序集合的操作

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.TreeSet;

/**
 *  JDK 21 之前,顺序集合中操作体验不一致
 *
 */
public class JEP431Test {
    public static void main(String[] args) {
        //    JDK 21 之前,顺序集合中操作体验不一致
        List<Integer> listTemp = List.of(1, 2, 3, 4, 5);

        ArrayList<Integer> list = new ArrayList(listTemp);
        Deque<Integer> deque = new ArrayDeque<>(listTemp);
        LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>(listTemp);
        TreeSet<Integer> sortedSet = new TreeSet<>(listTemp);
        LinkedHashMap<Integer, Integer> linkedHashMap = new LinkedHashMap<>();
        for (int i = 1; i <= 5; i++) {
            linkedHashMap.put(i, i);
        }

        // 输出第一个元素
        System.out.println(list.get(0));
        System.out.println(deque.getFirst());
        System.out.println(linkedHashSet.iterator().next());
        System.out.println(sortedSet.first());
        //System.out.println(linkedHashMap.firstEntry());没办法
        System.out.println("-----------------------");

        // 输出最后一个元素
        System.out.println(list.get(list.size()-1));
        System.out.println(deque.getLast());
        //System.out.println(linkedHashSet()); 没办法,只能遍历
        System.out.println(sortedSet.last());
        //System.out.println(linkedHashMap); 没办法
        System.out.println("-----------------------");

        // 逆序遍历
        for (var it = list.listIterator(list.size()); it.hasPrevious();) {
            var e = it.previous();
            System.out.print(e);
        }
        System.out.println();
        for (var it = deque.descendingIterator(); it.hasNext();) {
            var e = it.next();
            System.out.print(e);
        }
        System.out.println();
        for (Integer i : sortedSet.descendingSet()) {
            System.out.print(i);
        }
        System.out.println();
        // sortedSet linkedHashMap 逆序输出很难操作
    }
}

运行输出:

$ java ./JEP431Test.java
1
1
1
1
-----------------------
5
5
5
-----------------------
54321
54321
54321

JDK 21 有序集合操作

从 JDK 21 开始,增加了 SequencedCollection 接口和 SequencedSet 接口以及SequencedMap 接口,且 在 SequencedCollection 接口定义了有序集合集中常用的操作方法。

addFirst
addLast
getFirst
getLast
removeFirst
removeLast
reversed

在 SequencedMap 接口中也增加了有序 Map 的常用操作。

firstEntry
lastEntry
pollFirstEntry
pollLastEntry
putFirst
putLast
reversed
sequencedEntrySet
sequencedKeySet
sequencedValues

那么在 JDK 21 中,针对有序集合的操作体验就非常一致了。

// JDK 21 之后,为所有元素插入有序集合提供了一致的操作 API
List<Integer> listTemp = List.of(1, 2, 3, 4, 5);

ArrayList<Integer> list = new ArrayList(listTemp);
Deque<Integer> deque = new ArrayDeque<>(listTemp);
LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>(listTemp);
TreeSet<Integer> sortedSet = new TreeSet<>(listTemp);
LinkedHashMap<Integer, Integer> linkedHashMap = new LinkedHashMap<>();
for (int i = 1; i <= 5; i++) {
    linkedHashMap.put(i, i);
}

// 输出第一个元素
System.out.println(list.getFirst());
System.out.println(deque.getFirst());
System.out.println(linkedHashSet.getFirst());
System.out.println(sortedSet.getFirst());
System.out.println(linkedHashMap.firstEntry());
System.out.println("-----------------------");

// 输出最后一个元素
System.out.println(list.getLast());
System.out.println(list.getLast());
System.out.println(deque.getLast());
System.out.println(sortedSet.getLast());
System.out.println(linkedHashMap.lastEntry());
System.out.println("-----------------------");

// 逆序遍历
Consumer<SequencedCollection> printFn = s -> {
    // reversed 逆序元素
    s.reversed().forEach(System.out::print);
    System.out.println();
};
printFn.accept(list);
printFn.accept(deque);
printFn.accept(linkedHashSet);
printFn.accept(sortedSet);
// 有序 map 接口是 SequencedMap,上面的 consume类型不适用
linkedHashMap.reversed().forEach((k, v) -> {
    System.out.print(k);
});

输出结果:

1
1
1
1
1=1
-----------------------
5
5
5
5
5=5
-----------------------
54321
54321
54321
54321
54321

Record 模式

Record 模式在 Java 14 中就已经引入,在 Java 16 中对 Record 可以进行 instanceof 匹配且可以进行简单结构,像下面这样:

public class Jep440Record {
    public static void main(String[] args) {

        Dog dog = new Dog("Husky", 1);
        if (dog instanceof Dog(String name, int age)) {
            String res = StringTemplate.STR."name:\{name} age:\{age}";
            System.out.println(res);
        }
      
    }
}
record Dog(String name, int age) {}

输出:name:Husky age:1

而现在,可以对更复杂嵌套的 Record,进行解构。

public class Jep440Record {
    public static void main(String[] args) {
        Dog dog = new Dog("Husky", 1);
        Object myDog = new MyDog(dog, Color.BLACK);
        if (myDog instanceof MyDog(Dog(String name,int age),Color color)){
            String res = StringTemplate.STR."name:\{name} age:\{age} color:\{color}";
            System.out.println(res);
        }
    }
}
record Dog(String name, int age) {}
enum Color{WHITE,GREY,BLACK};
record MyDog(Dog dog,Color color){};

输出:name:Husky age:1 color:BLACK

switch 模式匹配

在 Java 21 中,switch 可以用于匹配类型了。

public class Jep441SwitchPatten {

    public static void main(String[] args) {
        String r1 = formatterPatternSwitch(Integer.valueOf(1));
        String r2 = formatterPatternSwitch(new String("ahzoo.cn"));
        String r3 = formatterPatternSwitch(Double.valueOf(3.14D));
        System.out.println(r1);
        System.out.println(r2);
        System.out.println(r3);
    }

    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();
        };
    }
}

输出:

int 1
String ahzoo.cn
double 3.140000

Java 21 中 switch 还可以用于 null 判断。

static void testFooBarNew(String s) {
    switch (s) {
        case null         -> System.out.println("Oops");
        case "Foo", "Ahzoo" -> System.out.println("Great");
        default           -> System.out.println("Ok");
    }
}

预览功能

  • 字符串模板 (预览)

  • 未命名模式的变量 (预览)

  • 未命名类和 main 方法 (预览)

版权声明
本文依据 CC-BY-NC-SA 4.0 许可协议授权,请您在转载时注明文章来源为 Z次元 ,若本文涉及转载第三方内容,请您一同注明。
更多专栏文章推荐
知识积累
人非生而知之者,孰能无惑?惑而不从师,其为惑也,终不解矣。
JDK新版特性(11-17)
2025/1/30
JDK新版特性(9-11)
2025/1/30
评论区
发表评论

这里还没有评论哦

快来发一条评论抢占前排吧

JDK18
JDK19
JDK20
JDK21
虚拟线程
引入虚拟线程原因
虚拟线程的使用
创建提交执行虚拟线程
设置虚拟线程名称
启动为虚拟线程
判断是否是虚拟线程
有序集合
JDK 21 之前有序集合操作
JDK 21 有序集合操作
Record 模式
switch 模式匹配
预览功能
目录
JDK18
JDK19
JDK20
JDK21
虚拟线程
引入虚拟线程原因
虚拟线程的使用
创建提交执行虚拟线程
设置虚拟线程名称
启动为虚拟线程
判断是否是虚拟线程
有序集合
JDK 21 之前有序集合操作
JDK 21 有序集合操作
Record 模式
switch 模式匹配
预览功能
十玖八柒
每天进步多一点
欢迎到访φ(゜▽゜*)♪
最新评论
个人占星:

想给自己的网站弄个统计功能,但不会弄,头疼

永恒末匕:

好哇塞,这个厉害

十玖八柒:

测试图片发送

Corwin: @十玖八柒

哎 主要是我的个人网站用的是静态的cos 实现评论框还是有点困难

我的
个人主页
站点地图
RSS订阅
导航
十年之约
虫洞穿梭
全站友链
虚位以待
©2020 - 2025 By 十玖八柒 版权所有
豫ICP备20021466号