【java】JDK21 要来了

文章目录

  • 前言
  • 更丝滑的并发编程模式
    • 虚拟线程(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方法获取当前登录用户的值了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

大数据分析案例-基于LightGBM算法构建航空公司满意度预测模型

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

Nexus搭建Maven私有库介绍

为什么需要Maven私有库&#xff1f; 使用Maven获取Java依赖包的时候&#xff0c; 默认是从Maven的中央库下载 jar文件&#xff0c; 中央库的地址是&#xff1a; https://repo.maven.apache.org/maven2 。 如果下载速度慢&#xff0c; 可以使用阿里的镜像&#xff0c; 地址如下…

【服务器数据恢复】HP LeftHand存储raid5不可用的数据恢复案例

HP LeftHand存储简介&#xff1a; HP LeftHand存储支持搭建RAID5、RAID6、RAID10磁盘阵列&#xff0c;支持卷快照&#xff0c;卷动态扩容等。服务端和客户端分别如下&#xff1a; LeftHand存储共有三个级别&#xff1a;物理磁盘、基于多个物理磁盘组成的逻辑磁盘&#xff08;ra…

浏览器渲染流程

解析HTML&#xff0c;生成DOM树&#xff0c;解析CSS&#xff0c;生成CSSOM树 将DOM树和CSSOM树结合&#xff0c;生成渲染树(Render Tree) Layout(回流):根据生成的渲染树&#xff0c;进行回流(Layout)&#xff0c;得到节点的几何信息&#xff08;位置&#xff0c;大小&#x…

Selenium+Pytest自动化测试框架实战

前言 Selenium和Pytest是非常流行的自动化测试框架&#xff0c;二者的配合可以有效地进行Web应用程序的测试。在使用Selenium和Pytest进行自动化测试时&#xff0c;可以实现自动化测试的模块化、重复使用&#xff0c;并且可以在现有的测试项目中方便地集成和调用。 测试框架简…

B树和B+树索引

B树索引 磁盘IO读数据到内存 malloc/new 向 kernel申请4B空间&#xff0c;实际系统可能给2个页面空间即8KB&#xff0c;剩下8KB-4B空间由 lib.so 或libc.so 的 ptmalloc 或tcmalloc管理 。这样就不需要每次去kernel申请内存 磁盘IO同理&#xff0c;我们读一部分数据会把一整…

学了Python后还用学R语言吗?

学习R语言是否有必要取决于你的具体需求和背景。虽然R语言和Python都是数据科学领域广泛使用的编程语言&#xff0c;但它们之间还是存在一些差异。 如果你主要从事数据分析、统计建模或者数据可视化等工作&#xff0c;那么学习R语言可能更为适合。R语言在数据处理和统计分析方…

02_类加载子系统

02_类加载子系统 一、内存结构概述二、类加载器与类的加载过程1、类加载器子系统的作用2、类加载器ClassLoader的作用3、类的加载过程 三、类加载器的分类1、启动类加载器2、扩展类加载器3、应用程序类加载器4、自定义类加载器5、获取ClassLoader的几种方式 五、双亲委派机制1、…

浅谈.NET语言开发应用领域

.NET语言是一种跨平台的开发框架&#xff0c;适用于各种应用程序的开发。以下是一些常见的.NET语言开发应用领域&#xff1a; 桌面应用程序开发&#xff1a;使用.NET框架开发的桌面应用程序可以在Windows操作系统上运行&#xff0c;包括Windows Forms和WPF。这些应用程序可以用…

第二章 进程的描述与控制

目录 一、进程的概念、组成、特征 1.1 进程的概念 1.1.1 程序 1.1.2 进程 1.2 进程的组成 1.3 进程控制块PCB 1.4 进程的特征 二、进程的状态与转换 2.1 进程的状态 2.2 创建态、就绪态 2.3 运行态 2.4 阻塞态 2.5 终止态 2.6 进程状态的转换 2.7 进程的组织 三…

【状态估计】无迹卡尔曼滤波(UKF)应用于FitzHugh-Nagumo神经元动力学研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【ProNoC】Chap.1 ProNoC生成2x2的mesh型的4核片上网络系统;实现NoC的RTL设计

【ProNoC】Chap.1 ProNoC生成2x2的mesh型的4核片上网络系统&#xff1b;实现NoC的RTL设计 0. NoC多核片上网络生成器ProNoCProNoC的功能实现 1. 生成一个叫做Mor1kx SoC的单个Tile&#xff08;包含NI网络接口&#xff09;1.1 打开ProNoC用于生成Tile的GUI界面1.2 为Tile添加时钟…

团体程序设计天梯赛-练习集L1篇①

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;Hello大家好呀&#xff0c;我是陈童学&#xff0c;一个与你一样正在慢慢前行的普通人。 &#x1f3c0;个人主页&#xff1a;陈童学哦CSDN &#x1f4a1;所属专栏&#xff1a;PTA &#x1f381;希望各…

【Servlet学习一】认识Servlet 创建第一个Servlet项目

目录 &#x1f31f;需要知道&#xff1a; &#x1f308;1、Tomcat是什么&#xff1f; &#x1f308; 2、Maven &#x1f31f;一、认识Servlet &#x1f308;1、Servlet是什么&#xff1f; &#x1f308;2、实现第一个Servlet项目。 &#x1f308;3、简单了解Postman工具…

LTV-6314-ASEMI代理台湾光宝高速光耦LTV-6314

编辑&#xff1a;ll LTV-6314-ASEMI代理台湾光宝高速光耦LTV-6314 型号&#xff1a;LTV-6314 品牌&#xff1a;台湾光宝 封装&#xff1a;LSOP-6 引脚数量&#xff1a;6 类型&#xff1a;光耦 特性&#xff1a;台湾光宝、IGBT驱动器、储能专用光耦&#xff3c;高速光耦 …

云服务器ECS_云主机_服务器托管_弹性计算-阿里云

阿里云服务器ECS&#xff08;Elastic Compute Service&#xff09;是一种安全可靠、弹性可伸缩的云计算服务&#xff0c;阿里云提供多种云服务器ECS实例规格&#xff0c;如通用算力型u1、ECS计算型c7、通用型g7、GPU实例等&#xff0c;阿里云服务器网分享阿里云服务器ECS详细介…

基于深度学习的高精度鸽子检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度鸽子检测识别系统可用于日常生活中或野外来检测与定位鸽子目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的鸽子目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检测模型…

Java8 List集合如何指定打印分隔符

目录 背景方法一&#xff1a;String.join&#xff08;推荐&#xff09;方法二&#xff1a;Collectors.joining总结 背景 无论是在学习还是日常的应用开发过程中&#xff0c;我们经常会需要使用分隔符将 List 集合打印出来。 如下所示&#xff1a; import java.util.Arrays;pub…

GPT模型支持下的Python-GEE遥感云大数据分析、管理与可视化技术及多领域案例实践

随着航空、航天、近地空间等多个遥感平台的不断发展&#xff0c;近年来遥感技术突飞猛进。由此&#xff0c;遥感数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量也大幅增长&#xff0c;使其越来越具有大数据特征。对于相关研究而言&#xff0c;遥感大数据的出现为其提…

基于flask的web应用开发——搭建一个云盘

目录 0. 前言1. 实现开放下载链接2. 稍加改装3. 效果演示4. 云服务器项目部署 0. 前言 本节利用 flask 开放下载链接 操作系统&#xff1a;Windows10 家庭版 开发环境&#xff1a;Pycahrm Comunity 2022.3 Python解释器版本&#xff1a;Python3.8 第三方库&#xff1a;fla…