JDK21 新特性

1. 虚拟线程(Virtual Threads)

https://openjdk.org/jeps/444

  • 虚拟线程是工作在平台线程之上的,虚拟线程的数量可以远大于操作系统线程的数量,多个虚拟线程挂载在一个平台线程上,一个平台线程可以在不同的时间执行不同的虚拟线程,当虚拟线程被阻塞或等待时,平台线程可以切换到执行另一个虚拟线程。

请添加图片描述

注意事项:

  1. 虚拟线程主要作用是提升服务器端的吞吐量,而不是提高线程的执行速度。
  2. 虚拟线程只适合IO密集型的任务,阻塞期间可以将CPU资源让渡给其他任务,不适合CPU密集型的任务或非阻塞任务。
  3. 虚拟线程是廉价且轻量级的,使用完后立即被销毁,因此不需要池化。
  • 创建虚拟线程的方法
import lombok.extern.slf4j.Slf4j;

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

@Slf4j
public class Demo {

    public static void main(String[] args) throws Exception {
        Runnable task = () -> {
            log.info(Thread.currentThread().toString());
        };
        // 1. 通过 Thread.ofVirtual() 创建
        Thread thread = Thread.ofVirtual().name(Thread.currentThread().toString()).start(task);
        // 2. 通过 Thread.startVirtualThread() 创建
        Thread.startVirtualThread(task);
        // 3. 通过 ThreadFactory 创建
        ThreadFactory factory = Thread.ofVirtual().factory();
        Thread newThread = factory.newThread(task);
        newThread.start();
        // 4. 通过 Executors.newVirtualThreadPerTaskExecutor() 创建
        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            IntStream.range(0, 5).forEach(i -> {
                executor.submit(() -> {
                    log.info("{}, before", Thread.currentThread());
                    try {
                        Thread.sleep(Duration.ofSeconds(1));
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    log.info("{}, after", Thread.currentThread());
                });
            });
        }

        System.in.read();
    }

}

  • 打印
19:49:16.027 [] INFO org.example.virtual_thread.Demo -- VirtualThread[#22]/runnable@ForkJoinPool-1-worker-2
19:49:16.027 [Thread[#1,main,5,main]] INFO org.example.virtual_thread.Demo -- VirtualThread[#20,Thread[#1,main,5,main]]/runnable@ForkJoinPool-1-worker-1
19:49:16.029 [] INFO org.example.virtual_thread.Demo -- VirtualThread[#23]/runnable@ForkJoinPool-1-worker-2
19:49:16.029 [] INFO org.example.virtual_thread.Demo -- VirtualThread[#25]/runnable@ForkJoinPool-1-worker-1, before
19:49:16.030 [] INFO org.example.virtual_thread.Demo -- VirtualThread[#27]/runnable@ForkJoinPool-1-worker-2, before
19:49:16.029 [] INFO org.example.virtual_thread.Demo -- VirtualThread[#26]/runnable@ForkJoinPool-1-worker-2, before
19:49:16.031 [] INFO org.example.virtual_thread.Demo -- VirtualThread[#28]/runnable@ForkJoinPool-1-worker-1, before
19:49:16.031 [] INFO org.example.virtual_thread.Demo -- VirtualThread[#29]/runnable@ForkJoinPool-1-worker-1, before
19:49:17.033 [] INFO org.example.virtual_thread.Demo -- VirtualThread[#27]/runnable@ForkJoinPool-1-worker-1, after
19:49:17.033 [] INFO org.example.virtual_thread.Demo -- VirtualThread[#25]/runnable@ForkJoinPool-1-worker-2, after
19:49:17.033 [] INFO org.example.virtual_thread.Demo -- VirtualThread[#28]/runnable@ForkJoinPool-1-worker-2, after
19:49:17.033 [] INFO org.example.virtual_thread.Demo -- VirtualThread[#26]/runnable@ForkJoinPool-1-worker-1, after
19:49:17.033 [] INFO org.example.virtual_thread.Demo -- VirtualThread[#29]/runnable@ForkJoinPool-1-worker-2, after
  • 通过25号虚拟线程可以看出,阻塞前后使用的平台线程发生过切换

在这里插入图片描述

  • VirtualThread[#22]/runnable@ForkJoinPool-1-worker-2表示VirtualThread[#22]这个虚拟线程依赖于平台线程ForkJoinPool-1-worker-2,这里我在VM参数中添加了-Djdk.virtualThreadScheduler.maxPoolSize=2 -Djdk.virtualThreadScheduler.parallelism=2,限制平台线程最大2个,所以只能会看到ForkJoinPool-1-worker-1和ForkJoinPool-1-worker-2两个平台线程。

在这里插入图片描述

  • 虚拟线程在block后,会与平台线程解绑,然后加入任务列表排队,等待分配给平台线程

在这里插入图片描述

2. 有序集合(Sequenced Collections)

https://openjdk.org/jeps/431

  • Sequenced Collections是一种具有确定出现顺序的集合。提供了处理集合第一个和最后一个元素以及反转集合的简单方法。

Sequenced Collections 包括以下三个接口:

  1. SequencedCollection
  2. SequencedSet
  3. SequencedMap

在这里插入图片描述

interface SequencedCollection<E> extends Collection<E> {

  // New Method

  SequencedCollection<E> reversed();

  // Promoted methods from Deque<E>

  void addFirst(E);
  void addLast(E);

  E getFirst();
  E getLast();

  E removeFirst();
  E removeLast();
}

3. switch 的模式匹配(Pattern Matching for switch)

https://openjdk.org/jeps/441

  • 属于语法方面的升级,增强了 switch 表达式,允许在 case 标签中使用模式。
  • JDK21之前
// Prior to Java 21
static String formatter(Object obj) {
    String formatted = "unknown";
    if (obj instanceof Integer i) {
        formatted = String.format("int %d", i);
    } else if (obj instanceof Long l) {
        formatted = String.format("long %d", l);
    } else if (obj instanceof Double d) {
        formatted = String.format("double %f", d);
    } else if (obj instanceof String s) {
        formatted = String.format("String %s", s);
    }
    return formatted;
}
  • JDK21之后
// As of Java 21
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();
    };
}

4. 记录模式(Record Patterns)

https://openjdk.org/jeps/440

  • 记录模式(Record Patterns) 可对 record 的值进行解构,也就是更方便地从记录类(Record Class)中提取数据。并且,记录模式和类型模式可以嵌套使用,以实现强大的、声明性的和可组合的数据导航和处理形式。
  • 记录模式不能单独使用,而是要与 instanceof 或 switch 模式匹配一同使用。
  1. 以 instanceof 为例
// 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);
    }
}

5. ZGC

https://openjdk.org/jeps/439

  • ZGC是一款低延迟、高吞吐的JVM垃圾回收器。JDK11试验版本,JDK15变为生产版本,但都是不分代的,所有的对象都存储在一起,每次垃圾回收都要回收所有对象。
    在JDK21中区分年轻代和老年代,因为年轻代的对象存活时间更短,所以 通过提高收集年轻代的对象的频率迅速释放JVM内存。

  • 默认不开启ZGC,可以使用以下命令开启

java -XX:+UseZGC -XX:+ZGenerational 

6. 准备禁用动态代理(Prepare to Disallow the Dynamic Loading of Agents)

https://openjdk.org/jeps/451

  • 当代理被动态加载到运行中的 JVM 时发出警告。这些警告的目的是让用户做好准备,以便在未来的版本中默认禁止动态加载代理。
    动态代理允许开发人员在运行时修改和监视Java应用程序,存在潜在的安全风险,例如窃取敏感信息、篡改数据等。因此,为了加强Java应用程序的安全性,限制动态加载代理的使用是有必要的。
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
  • 21
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
# jdk7新特性 ## try-with-resources 是一种声明了`一种或多种资源的try语句`。资源是指在程序用完了之后必须要关闭的对象。try-with-resources语句保证了每个声明了的`资源在语句结束的时候都会被关闭`。任何实现了java.lang.`AutoCloseable`接口的对象,和实现了java .io .`Closeable`接口的对象,`都可以当做资源使用`。 ``` try ( InputStream is = new FileInputStream("xx"); OutputStream os = new FileOutputStream("xx") ) { //xxx //不用关闭了,JVM帮你关闭流 ``` ## 多异常统一处理 在Java 7中,catch代码块得到了升级,用以在`单个catch块中处理多个异常`。如果你要捕获多个异常并且它们包含相似的代码,使用这一特性将会减少代码重复度。 ``` try { //xxx } catch (AException | BException e) { e.printStackTrace(); } ``` 缺点是异常处理细粒度降低 ## 泛型推导 ``` List<String> list = new ArrayList<>(); ``` `泛型实例`的创建可以通过`类型推断`来简化,`可以去掉`后面new部分的泛型类型,`只用<>`就可以了。 ## 使用ForkJoin Java 7开始引入了一种新的Fork/Join线程池,它可以执行一种特殊的任务:把一个大任务拆成多个小任务并行执行。 我们举个例子:如果要计算一个超大数组的和,最简单的做法是用一个循环在一个线程内完成: ```ascii ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘ ``` 还有一种方法,可以把数组拆成两部分,分别计算,最后加起来就是最终结果,这样可以用两个线程并行执行: ```ascii ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘ ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘ ``` 如果拆成两部分还是很大,我们还可以继续拆,用4个线程并行执行: ```ascii ┌─┬─┬─┬─┬─┬─┐ └─┴─┴─┴─┴─┴─┘ ┌─┬─┬─┬─┬─┬─┐ └─┴─┴─┴─┴─┴─┘ ┌─┬─┬─┬─┬─┬─┐ └─┴─┴─┴─┴─┴─┘ ┌─┬─┬─┬─┬─┬─┐ └─┴─┴─┴─┴─┴─┘ ``` 这就是Fork/Join任务的原理:判断一个任务是否足够小,如果是,直接计算,否则,就分拆成几个小任务分别计算。这个过程可以反复“裂变”成一系列小任务。 我们来看如何使用Fork/Join对大数据进行并行求和: ``` public class Main { public static void main(String[] args) throws Exception { // 创建2000个随机数组成的数组: long[] array = new long[2000]; long expectedSum = 0; for (int i = 0; i < array.length; i++) { array[i] = random(); expectedSum += array[i]; } System.out.println("Expected sum: " + expectedSum); // fork/join: ForkJoinTask<Long> task = new SumTask(array, 0, array.length); long startTime = System.currentTimeMillis(); Long result = ForkJoinPool.commonPool().invoke(task); long endTime = System.currentTimeMillis(); System.out.println("Fork

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值