Z次元
文章 笔记 日志
专题
专栏分类
文章归档
友链
友情链接
朋友圈
留言
头像
系列文章
JDK新版特性(9-11)
2025/1/30
JDK新版特性(18-21)
2025/2/7
JDK新版特性(12-17)
2025/1/30
JDK新版特性(22-25)
2025/11/9
JDK新版特性(18-21)
系列文章
知识积累
更新于:2025/2/16
|
发布于:2025/2/7
文章摘要
......

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新版特性(12-17)
2025/1/30
JDK新版特性(9-11)
2025/1/30
JDK8升级JDK21报错解决方案记录
2025/12/28
MySQL模糊查询的多种方式
2025/12/4
评论区

删除确认

评论删除后无法恢复,请确认是否继续?
发表评论

这里还没有评论哦

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

目录
1
JDK18
2
JDK19
3
JDK20
4
JDK21
虚拟线程
引入虚拟线程原因
虚拟线程的使用
创建提交执行虚拟线程
设置虚拟线程名称
启动为虚拟线程
判断是否是虚拟线程
有序集合
JDK 21 之前有序集合操作
JDK 21 有序集合操作
5
Record 模式
switch 模式匹配
预览功能
目录
1
JDK18
2
JDK19
3
JDK20
4
JDK21
虚拟线程
引入虚拟线程原因
虚拟线程的使用
创建提交执行虚拟线程
设置虚拟线程名称
启动为虚拟线程
判断是否是虚拟线程
有序集合
JDK 21 之前有序集合操作
JDK 21 有序集合操作
5
Record 模式
switch 模式匹配
预览功能
博客
文章 笔记 日志
专题
专栏分类 文章归档
友链
友情链接 朋友圈
交流
留言 关于我
主页
菜单
置顶
主题
我的
十玖八柒
每天进步多一点
欢迎到访φ(゜▽゜*)♪
最新评论
柒: @Honesty
因为这个系列主要是总结和代码相关的特性,不过这个特性感觉确实可以写一下
Honesty:
哎?Jdk25 对象头压缩这个很重要的特性没记呢
个人占星:
DeepSeek没有想象中的好用
个人占星:
想给自己的网站弄个统计功能,但不会弄,头疼
我的
关于我
个人主页
站点地图
RSS订阅
导航
十年之约
虫洞穿梭
开源博客
前端开源仓库
后端开源仓库
©2020 - 2026 By 十玖八柒 版权所有
豫ICP备20021466号