【java】JDK21 要来了

news2024/11/18 12:35:29

文章目录

  • 前言
  • 更丝滑的并发编程模式
    • 虚拟线程(Virtual Threads)
    • 结构化并发(Structured Concurrency)
    • 作用域值(Scoped Values)
  • 试验一下
  • 虚拟线程的例子
  • 结构化编程的例子
  • Scoped Values 的例子

前言

不过多久,JDK 21 马上就出来了,看来 Java 这几年真的是长进了。
目前 Java 的最新稳定版是 JDK 20,但这是个过渡版,JDK21就是 LTS 版的了,也快要发布了,在今年9月份(也就是2023年9月)就要正式发布了。
但是,猜都不用猜,你肯定还在用 Java 8 吧!

更丝滑的并发编程模式

如果说之前的 JDK17你还觉得没必要折腾,那 JDK21确实有必要关注一下了。因为 JDK21 引入了一种新型的并发编程模式。

当前 Java 中的多线程并发编程绝对是另我们都非常头疼的一部分,感觉就是学起来难啃,用起来难用。但是转头看看使用其他语言的朋友们,根本就没有这个烦恼嘛,比如 GoLang,感觉人家用起来就很丝滑呢。

JDK21 中就在这方面做了很大的改进,让Java并发编程变得更简单一点,更丝滑一点。确切的说,在 JDK19或JDK20中就有这些改进了。

那具体是什么呢?让我们来具体来看一下。下面是JDK21的 Feature。
在这里插入图片描述

其中Virtual Threads、Scoped Values、Structured Concurrency就是针对多线程并发编程的几个功能。我们今天也主要来说一下他们。

虚拟线程(Virtual Threads)

虚拟线程是基于协程的线程,它们与其他语言中的协程具有相似之处,但也存在一些不同之处。
虚拟线程是依附于主线程的,如果主线程销毁了,那虚拟线程也不复存在。

  • 相同之处:

    • 虚拟线程和协程都是轻量级的线程,它们的创建和销毁的开销都比传统的操作系统线程要小。
    • 虚拟线程和协程都可以通过暂停和恢复来实现线程之间的切换,从而避免了线程上下文切换的开销。
    • 虚拟线程和协程都可以使用异步和非阻塞的方式来处理任务,提高应用程序的性能和响应速度。
  • 不同之处:

    • 虚拟线程是在 JVM 层面实现的,而协程则是在语言层面实现的。因此,虚拟线程的实现可以与任何支持 JVM 的语言一起使用,而协程的实现则需要特定的编程语言支持。
    • 虚拟线程是一种基于线程的协程实现,因此它们可以使用线程相关的 API,如 ThreadLocal、Lock 和 Semaphore。而协程则不依赖于线程,通常需要使用特定的异步编程框架和 API。
    • 虚拟线程的调度是由 JVM 管理的,而协程的调度是由编程语言或异步编程框架管理的。因此,虚拟线程可以更好地与其他线程进行协作,而协程则更适合处理异步任务。

总的来说,虚拟线程是一种新的线程类型,它可以提高应用程序的性能和资源利用率,同时也可以使用传统线程相关的 API。虚拟线程与协程有很多相似之处,但也存在一些不同之处。

虚拟线程确实可以让多线程编程变得更简单和更高效。相比于传统的操作系统线程,虚拟线程的创建和销毁的开销更小,线程上下文切换的开销也更小,因此可以大大减少多线程编程中的资源消耗和性能瓶颈。

使用虚拟线程,开发者可以像编写传统的线程代码一样编写代码,而无需担心线程的数量和调度,因为 JVM 会自动管理虚拟线程的数量和调度。此外,虚拟线程还支持传统线程相关的 API,如 ThreadLocal、Lock 和 Semaphore,这使得开发者可以更轻松地迁移传统线程代码到虚拟线程。

虚拟线程的引入,使得多线程编程变得更加高效、简单和安全,使得开发者能够更加专注于业务逻辑,而不必过多地关注底层的线程管理。

结构化并发(Structured Concurrency)

结构化并发是一种编程范式,旨在通过提供结构化和易于遵循的方法来简化并发编程。使用结构化并发,开发人员可以创建更容易理解和调试的并发代码,并且不容易出现竞争条件和其他与并发有关的错误。在结构化并发中,所有并发代码都被结构化为称为任务的定义良好的工作单元。任务以结构化方式创建、执行和完成,任务的执行总是保证在其父任务完成之前完成。

Structured Concurrency(结构化并发)可以让多线程编程更加简单和可靠。在传统的多线程编程中,线程的启动、执行和结束是由开发者手动管理的,因此容易出现线程泄露、死锁和异常处理不当等问题。

使用结构化并发,开发者可以更加自然地组织并发任务,使得任务之间的依赖关系更加清晰,代码逻辑更加简洁。结构化并发还提供了一些异常处理机制,可以更好地管理并发任务中的异常,避免因为异常而导致程序崩溃或数据不一致的情况。

除此之外,结构化并发还可以通过限制并发任务的数量和优先级,防止资源竞争和饥饿等问题的发生。这些特性使得开发者能够更加方便地实现高效、可靠的并发程序,而无需过多关注底层的线程管理。

作用域值(Scoped Values)

作用域值是JDK 20中的一项功能,允许开发人员创建作用域限定的值,这些值限定于特定的线程或任务。作用域值类似于线程本地变量,但是设计为与虚拟线程和结构化并发配合使用。它们允许开发人员以结构化的方式在任务和虚拟线程之间传递值,无需复杂的同步或锁定机制。作用域值可用于在应用程序的不同部分之间传递上下文信息,例如用户身份验证或请求特定数据。

试验一下

进行下面的探索之前,你要下载至少 JDK19或者直接下载 JDK20,JDK 20 目前(截止到2023年9月份)是正式发布的最高版本,如果你用 JDK 19的话,没办法体验到Scoped Values的功能。
在这里插入图片描述

或者是直接下载 JDK 21 的 Early-Access Builds(早期访问版本)。在这个地址下载 「jdk.java.net/21/」,下载对应的版…

如果你用的是 IDEA ,那你的IDEA 版本最起码是2022.3 这个版本或者之后的,否则不支持这么新的 JDK 版本。
在这里插入图片描述
如果你用的是 JDK19或者 JDK20的话,要在你的项目设置中将 language level设置为19或20的 Preview 级别,否则编译的时候会提示你无法使用预览版的功能,虚拟线程就是预览版的功能。
在这里插入图片描述

如果你用的是 JDK21的话,将 language level 设置为 X -Experimental Features,另外,因为 JDK21不属于正式版本,所以需要到 IDEA 的设置中(注意是 IDEA 的设置,不是项目的设置了),将这个项目的 Target bytecode version手动修改为21,目前可选的最高就是20,也就是JDK20。设置为21之后,就可以使用 JDK21中的这些功能了。
在这里插入图片描述

虚拟线程的例子

我们现在启动线程是怎么做的呢?
先声明一个线程类,implements 自 Runnable,并实现 run方法。

public class SimpleThread implements Runnable{

    @Override
    public void run() {
        System.out.println("当前线程名称:" + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

然后就可以使用这个线程类,然后启动线程了。

Thread thread = new Thread(new SimpleThread());
thread.start();

中规中矩,没毛病。
有了虚拟线程之后呢,怎么实现呢?

Thread.ofPlatform().name("thread-test").start(new SimpleThread());

下面是几种使用虚拟线程的方式。

  1. 直接启动一个虚拟线程
Thread thread = Thread.startVirtualThread(new SimpleThread());
  1. 使用 ofVirtual(),builder 方式启动虚拟线程,可以设置线程名称、优先级、异常处理等配置
Thread.ofVirtual()
                .name("thread-test")
                .start(new SimpleThread());
//或者
Thread thread = Thread.ofVirtual()
  .name("thread-test")
  .uncaughtExceptionHandler((t, e) -> {
    System.out.println(t.getName() + e.getMessage());
  })
  .unstarted(new SimpleThread());
thread.start();
  1. 使用 Factory 创建线程
ThreadFactory factory = Thread.ofVirtual().factory();
Thread thread = factory.newThread(new SimpleThread());
thread.setName("thread-test");
thread.start();
  1. 使用 Executors 方式
ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();
Future<?> submit = executorService.submit(new SimpleThread());
Object o = submit.get();

结构化编程的例子

想一下下面这个场景,假设你有三个任务要同时进行,只要任意一个任务执行完成并返回结果了,那就可以直接用这个结果了,其他的两个任务就可以停止了。比如说一个天气服务,通过三个渠道获取天气情况,只要有一个渠道返回就可以了。
这种场景下, 在 Java 8 下应该怎么做呢,当然也可以了。

// 执行任务并返回 Future 对象列表
List<Future<String>> futures = executor.invokeAll(tasks);

// 等待任一任务完成并获取结果
String result = executor.invokeAny(tasks);

使用 ExecutorService的invokeAll和invokeAny实现,但是会有一些额外的工作,在拿到第一个结果后,要手动关闭另外的线程。

而 JDK21中呢,可以用结构化编程实现。
ShutdownOnSuccess捕获第一个结果并关闭任务范围以中断未完成的线程并唤醒调用线程。
适用于任意子任务的结果都可以直接使用,并且无需等待其他未完成任务的结果的情况。
它定义了获取第一个结果或在所有子任务失败时抛出异常的方法

public static void main(String[] args) throws IOException {
  try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
    Future<String> res1 = scope.fork(() -> runTask(1));
    Future<String> res2 = scope.fork(() -> runTask(2));
    Future<String> res3 = scope.fork(() -> runTask(3));
    scope.join();
    System.out.println("scope:" + scope.result());
  } catch (ExecutionException | InterruptedException e) {
    throw new RuntimeException(e);
  }
}

public static String runTask(int i) throws InterruptedException {
  Thread.sleep(1000);
  long l = new Random().nextLong();
  String s = String.valueOf(l);
  System.out.println("第" + i + "个任务:" + s);
  return s;
}

ShutdownOnFailure
执行多个任务,只要有一个失败(出现异常或其他主动抛出异常情况),就停止其他未执行完的任务,使用scope.throwIfFailed捕捉并抛出异常。
如果所有任务均正常,则使用 Feture.get() 或*Feture.resultNow() 获取结果

public static void main(String[] args) throws IOException {
  try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Future<String> res1 = scope.fork(() -> runTaskWithException(1));
    Future<String> res2 = scope.fork(() -> runTaskWithException(2));
    Future<String> res3 = scope.fork(() -> runTaskWithException(3));
    scope.join();
    scope.throwIfFailed(Exception::new);

    String s = res1.resultNow(); //或 res1.get()
    System.out.println(s);
    String result = Stream.of(res1, res2,res3)
      .map(Future::resultNow)
      .collect(Collectors.joining());
    System.out.println("直接结果:" + result);
  } catch (Exception e) {
    e.printStackTrace();
    //throw new RuntimeException(e);
  }
}

// 有一定几率发生异常
public static String runTaskWithException(int i) throws InterruptedException {
  Thread.sleep(1000);
  long l = new Random().nextLong(3);
  if (l == 0) {
    throw new InterruptedException();
  }
  String s = String.valueOf(l);
  System.out.println("第" + i + "个任务:" + s);
  return s;
}

Scoped Values 的例子

我们肯定都用过 ThreadLocal,它是线程本地变量,只要这个线程没销毁,可以随时获取 ThredLocal 中的变量值。Scoped Values 也可以在线程内部随时获取变量,只不过它有个作用域的概念,超出作用域就会销毁。

public class ScopedValueExample {
    final static ScopedValue<String> LoginUser = ScopedValue.newInstance();

    public static void main(String[] args) throws InterruptedException {
        ScopedValue.where(LoginUser, "张三")
                .run(() -> {
                    new Service().login();
                });

        Thread.sleep(2000);
    }

    static class Service {
        void login(){
            System.out.println("当前登录用户是:" + LoginUser.get());
        }
    }
}

上面的例子模拟一个用户登录的过程,使用 ScopedValue.newInstance()声明了一个 ScopedValue,用 ScopedValue.where给 ScopedValue设置值,并且使用 run 方法执行接下来要做的事儿,这样一来,ScopedValue就在 run() 的内部随时可获取了,在run方法中,模拟调用了一个service 的login方法,不用传递LoginUser这个参数,就可以直接通过LoginUser.get方法获取当前登录用户的值了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/660669.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法与数据结构——递归算法+回溯算法——八皇后问题

八皇后问题 八皇后问题是一个经典的回溯算法问题&#xff0c;目的是在88的国际象棋棋盘上放置八个皇后&#xff0c;使得没有皇后可以互相攻击&#xff08;即没有两个皇后在同一行、同一列或同一对角线上&#xff09;。 回溯算法是一种解决问题的算法&#xff0c;它通过尝试所有…

软件质量保障QA

软件质量保障 目录概述需求&#xff1a; 设计思路实现思路分析1.alibaba guileline2.ckeckstyle3.findBugs4.PMD5.SourceMononiot 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness…

2014年全国硕士研究生入学统一考试管理类专业学位联考英语(二)试题

2014年全国硕士研究生入学考试英语(二)试题 Section I Use of English Directions:   Read the following text. Choose the best word(s) for each numbered blank and mark A, B, C or D on ANSWER SHEET. (10 points)   Thinner isn’t always better. A number of st…

软考A计划-网络工程师-交换机与路由器的配置

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

MyBatis­-Plus入门

目录 1.特性&#xff1a; 2.mybatis-plus 快速使用 3.mybatis与mybatis-plus实现方式对比 4.BaseMapper接口介绍 5.mybatis-plus中常用的注解 7.全局ID生成策略 8.逻辑删除&#xff08;1&#xff1a;局部逻辑删除&#xff1b;2&#xff1a;全局逻辑删除&#xff09; 8.…

Java代码质量分析Sonar

目录 1. sonar安装1.1 简介1.1.1 客户端1.1.2 sonar 版本区分1.1.2.1 社区版1.1.2.2 开发者版1.1.2.3 企业版 1.2 安装部署1.2.1 修改文件句柄数1.2.2 创建挂载目录1.2.3 创建docker-compose.yml1.2.4 启动1.2.4.1 访问测试 1.2.5 安装插件1.2.5.1 汉化插件 1.3 静态分析插件介…

新手快速搭建springboot项目

一、创建项目 1.1、创建项目 1.2、配置编码 1.3、取消无用提示 1.4、取消无用参数提示 二、添加POM父依赖 <!-- 两种方式添加父依赖或者import方式 --> <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-p…

redis-conf

1 大小写不敏感 2 包含文件 3 网络 4 通用配置 5 快照 6 复制 -----待补充 7 安全 security 8 限制 clients 9 APPEND ONLY MODE aof模式

OpenCV项目开发实战-- 将一个三角形变形为另一个三角形 ( C++ / Python )代码实现

文末附基于Python和C++两种方式实现的测试代码下载链接 图 1:左图中蓝色三角形内的所有像素都已转换为右图中的蓝色三角形。 在本教程中,我们将看到如何将图像中的单个三角形变形为不同图像中的另一个三角形。 在计算机图形学中,人们一直在处理扭曲三角形,因为任何 3D 表…

hadoop集群三之hive安装

这里记录下自己使用虚拟机详细安装hive的过程&#xff0c;在安装hive之前需要保证咋们已经安装好了hadoop&#xff0c;没有的话可以参考我之前的安装的流程 安装mysql # 更新密钥 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022# 安装Mysql yum库 rpm -Uvh http…

初识 Linux 进程

问题 strace 输出中的 execve(...) 究竟是什么&#xff1f; 进程生命周期 操作系统内部定义了进程的不同状态 Linux 进程基本概念 进程是 Linux 任务的执行单元&#xff0c;也是 Linux 系统资源的分配单元 每个 Linux 应用程序运行后由一个或多个进程构成 每个 Linux 进程可…

对称密钥【密码学】(四)

一、前言 在使用单向散列函数校验数据完整性时&#xff0c;需要保证原始的散列值不能被更改。 因此&#xff0c;在很多场景下&#xff0c;我们并不能仅仅通过单向散列函数来解决完整性问题&#xff0c;还需要其他技术来解决这个问题&#xff0c;如加密技术。 本篇博客就来介绍…

CloudFlare系列--使用第三方来自定义CSDN的IP(蓝精灵)

原文网址&#xff1a;CloudFlare系列--使用第三方来自定义CSDN的IP(蓝精灵)_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍CloudFlare的CDN如何自定义第三方IP。 概述 CloudFlare官网接入域名的方式只能是 NS 接入&#xff0c;这样默认DNS服务器只能改为CloudFlare的DNS服…

从零开始Vue项目中使用MapboxGL开发三维地图教程(七)创建一个自动旋转的地球、添加一个3D模型、实现在两个地图之间滑动和同步来比较两个地图

目录 1、创建一个自动旋转的地球地图2、添加一个3D模型3、一个页面创建两个底图之间滑动 1、创建一个自动旋转的地球地图 实现功能&#xff1a;地球仪和camera动画结合在一起&#xff0c;创建旋转行星效果。 实现思路&#xff1a;通过在动画结束时调用easeTo&#xff0c;旋转…

软考A计划-网络工程师-IP,TCP,UDP,ICMP报头

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

YOLOv5/v7 添加注意力机制,30多种模块分析⑤,SOCA模块 ,SimAM模块

目录 一、注意力机制介绍1、什么是注意力机制&#xff1f;2、注意力机制的分类3、注意力机制的核心 二、SOCA模块1、SOCA模块的原理2、实验结果3、应用示例 三、SimAM模块1、SimAM模块的原理2、实验结果3、应用示例 大家好&#xff0c;我是哪吒。 &#x1f3c6;本文收录于&…

Vue 面试题

一、对于MVVM的理解&#xff1f; MVVM 是 Model-View-ViewModel 的缩写。 1、Model 代表数据模型&#xff0c;也可以在Model中定义数据修改和操作的业务逻辑。 2、View 代表UI 组件&#xff0c;它负责将数据模型转化成UI 展现出来。 3、ViewModel 监听模型数据的改变和控制…

【分布式系统】分布式唯一ID生成方案总结

目录 UUID实现 数据库生成IDsegment号段模式美团Leaf-segment号段模式Redis生成ID实现 zookeeper生成ID实现 snowflake雪花算法实现 Leaf-snowflake雪花算法百度UidGenerator滴滴TinyID双号段缓存多DB支持tinyid-client 参考 UUID 基本方法之一。 UUID(Universally Unique Id…

Java阿里巴巴代码规范

目录 1 编程规约1.1 方法参数类型必须一致&#xff0c;不要出现自动装箱拆箱操作1.1.1 反例1.1.2 正例 1.2 SimpleDateFormat是线程不安全的1.2.1 反例1.2.2 正例 1.3 使用equals方法应该注意空指针1.3.1 反例1.3.2 正例 2 异常日志2.1 事务场景中如果异常被被捕获要注意回滚2.…

2023 年最适用于工业物联网领域的三款开源 MQTT Broker

MQTT 最初作为一种轻量级的发布/订阅消息传递协议而设计&#xff0c;如今已经成为工业物联网&#xff08;IIoT&#xff09;和工业 4.0 发展的重要基础。它的意义在于实现了各类工业设备与云端的无缝连接&#xff0c;促进了运营技术&#xff08;OT&#xff09;和信息技术&#x…