JDK21 要来了,,你不会还在用 Java 8吧!

news2024/10/2 1:41:11

目前 Java 的最新稳定版是 JDK 20,但这是个过渡版,JDK21就是 LTS 版的了,也快要发布了,在今年9月份(也就是2023年9月)就要正式发布了。

但是,猜都不用猜,你肯定还在用 Java 8 吧!

1更丝滑的并发编程模式

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

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

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

那具体是什么呢?让我们来具体来看一下。下面是JDK21的 Feature。

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

虚拟线程(Virtual Threads)

虚拟线程是基于协程的线程,它们与其他语言中的协程具有相似之处,但也存在一些不同之处。

虚拟线程是依附于主线程的,如果主线程销毁了,那虚拟线程也不复存在。

相同之处:

  1. 虚拟线程和协程都是轻量级的线程,它们的创建和销毁的开销都比传统的操作系统线程要小。

  2. 虚拟线程和协程都可以通过暂停和恢复来实现线程之间的切换,从而避免了线程上下文切换的开销。

  3. 虚拟线程和协程都可以使用异步和非阻塞的方式来处理任务,提高应用程序的性能和响应速度。

不同之处:

  1. 虚拟线程是在 JVM 层面实现的,而协程则是在语言层面实现的。因此,虚拟线程的实现可以与任何支持 JVM 的语言一起使用,而协程的实现则需要特定的编程语言支持。

  2. 虚拟线程是一种基于线程的协程实现,因此它们可以使用线程相关的 API,如 ThreadLocalLockSemaphore。而协程则不依赖于线程,通常需要使用特定的异步编程框架和 API。

  3. 虚拟线程的调度是由 JVM 管理的,而协程的调度是由编程语言或异步编程框架管理的。因此,虚拟线程可以更好地与其他线程进行协作,而协程则更适合处理异步任务。

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

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

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

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

结构化并发(Structured Concurrency)

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

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

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

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

作用域值(Scoped Values)

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

2试验一下

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

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

如果你用的是 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中的这些功能了。

3虚拟线程的例子

我们现在启动线程是怎么做的呢?

先声明一个线程类,implementsRunnable,并实现 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());

2、使用 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();

3、使用 Factory 创建线程

ThreadFactory factory = Thread.ofVirtual().factory();
Thread thread = factory.newThread(new SimpleThread());
thread.setName("thread-test");
thread.start();

4、使用 Executors 方式

ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();
Future<?> submit = executorService.submit(new SimpleThread());
Object o = submit.get();

4结构化编程的例子

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

这种场景下, 在 Java 8 下应该怎么做呢,当然也可以了。

List<Future<String>> futures = executor.invokeAll(tasks);


String result = executor.invokeAny(tasks);

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

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


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

5Scoped 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.whereScopedValue设置值,并且使用 run 方法执行接下来要做的事儿,这样一来,ScopedValue就在 run() 的内部随时可获取了,在run方法中,模拟调用了一个service 的login方法,不用传递LoginUser这个参数,就可以直接通过LoginUser.get方法获取当前登录用户的值了。

也希望有收获的同学捧个场,转个发、点个在看,谢谢您嘞!我们下期再见。 公众号「古时的风筝」,专注于后端技术,尤其是 Java 及周边生态。文章会收录在 JavaNewBee[3] 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在里面。

参考资料

[1]

https://juejin.cn/post/7177550894316126269: https://juejin.cn/post/7177550894316126269

[2]

https://jdk.java.net/21/%E3%80%8D%EF%BC%8C%E4%B8%8B%E8%BD%BD%E5%AF%B9%E5%BA%94%E7%9A%84%E7%89%88%E6%9C%AC%E3%80%82: https://link.juejin.cn/?target=https%3A%2F%2Fjdk.java.net%2F21%2F%25E3%2580%258D%25EF%25BC%258C%25E4%25B8%258B%25E8%25BD%25BD%25E5%25AF%25B9%25E5%25BA%2594%25E7%259A%2584%25E7%2589%2588%25E6%259C%25AC%25E3%2580%2582

[3]

https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fhuzhicheng%2FJavaNewBee: https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fhuzhicheng%2FJavaNewBee

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

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

相关文章

【密评】商用密码应用安全性评估从业人员考核题库(七)

商用密码应用安全性评估从业人员考核题库&#xff08;七&#xff09; 国密局给的参考题库5000道只是基础题&#xff0c;后续更新完5000还会继续更其他高质量题库&#xff0c;持续学习&#xff0c;共同进步。 1501 判断题 在公钥密码体制中&#xff0c;使用接收方的公钥加密的消…

MySQL通用查询日志 general query log 详解

通用查询日志&#xff08;general query log&#xff09;用来记录用户的所有操作&#xff0c;包括启动和关闭MySQL服务、所有用户的连接开始时间和截止时间、发送给MySQL数据库服务器的所有SQL指令等。当我们的数据发生异常时&#xff0c;查看通用查询日志&#xff0c;还原操作…

Oracle笔记-对ROWNUM的一次理解(简单分页)

此博文记录时间&#xff1a;2023-05-05&#xff0c;发到互联网上是2023-10-09 这个在分页里面用得比较多&#xff0c;在MySQL中&#xff0c;通常使用limit去操作&#xff0c;而去感觉比较简单&#xff0c;Oracle中无此关键字。 通过查阅资料后&#xff0c;要实现分页需要用到…

论文阅读笔记(Clover: 计算与存储被动分离的分布式键值存储系统)

关于Disaggregating Persistent Memory and Controlling Them Remotely: An Exploration of Passive Disaggregated Key-Value Stores这篇论文的笔记 原文链接 提出背景 传统的分布式存储系统中&#xff0c;每个节点都会包含计算和存储两个部分&#xff0c;一个节点既可以访…

分割等和子集

题目链接 分割等和子集 题目描述 注意点 数组 nums 非空数组 nums 只包含正整数 解答思路 最初想到的是根据回溯剪枝解决本题&#xff0c;如果数组大小小于2&#xff0c;则肯定不能找到分割等和子集&#xff0c;除此以外&#xff0c;如果数组之和sum不能被2整除&#xff0…

VMvare虚拟机安装国产麒麟V10桌面操作系统

一、系统下载 进入银河麒麟官网&#xff1a;https://www.kylinos.cn/ 选择桌面操作系统&#xff0c;然后进入操作系统版本选择页面&#xff0c;选择银河麒麟桌面操作系统V10 选择后&#xff0c;进入系统介绍页面&#xff0c;然后点击申请试用 点击后进入申请页面&#xf…

Apache Solr9.3 快速上手

Apache Solr 简介 Solr是Apache的顶级开源项目&#xff0c;使用java开发 &#xff0c;基于Lucene的全文检索服务器。 Solr比Lucene提供了更多的查询语句&#xff0c;而且它可扩展、可配置&#xff0c;同时它对Lucene的性能进行了优化。 安装 下载 : 下载地址解压 : tar -zxv…

uniapp apple 苹果登录 离线本地打包

官方文档 uni-app官网 文档写的不全&#xff0c;没有写离线打包流程 加lib 签名里带 sign in with apple hbuilder开关 代码 测试代码&#xff0c;获取app里所有的provider uni.getProvider({service: oauth,success: function (res) {console.log(res.provider)uni.showT…

ctfshow-web4(文件包含日志注入)

像web3那样使用php伪协议&#xff0c;回显error 看了看提示&#xff1a;日志注入 文件包含 使用Wappalyzer查看一下&#xff0c;使用的中间件是Ngnix 日志包含漏洞的成因还是服务器没有进行严格的过滤 &#xff0c;导致用户可以进行任意文件读取&#xff0c; 但是前提是服务器…

月报总结|Moonbeam 9月份大事一览

相信社区的小伙伴都过了一个圆满的中秋节&#xff0c;月圆人团圆&#xff0c;以月光代表生态的Moonbeam网络也如月亮一般&#xff0c;从始至终都在波卡。 9月&#xff0c;USDC上线波卡增加流动性、Moonbeam治理界面简化促进社区参与、Grant发放引爆Moonbeam Ignite再次回归、M…

QT基础入门——Qt事件(五)

前言&#xff1a; 事件&#xff08;event&#xff09;是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘&#xff0c;或者是窗口需要重新绘制的时候&#xff0c;都会发出一个相应的事件。一些事件在对用户操作做出响应时发出&#xff0c;如键盘事件等&#x…

(一)Apache log4net™ 手册 - 介绍

0、相关概念 Log4j 几乎每个大型应用程序都包含自己的日志记录或跟踪 API。根据这一规则&#xff0c;E.U. SEMPER &#x1f339;项目决定编写自己的跟踪 API。那是在 1996 年初。经过无数次的增强、几个化身和大量的工作&#xff0c;API 已经发展成为 log4j —— 一个流行的 Ja…

开源音乐播放器!

导读音乐是生活的一部分。维基百科关于音乐发展历史的文章有这样一段不错的描述说&#xff1a;“全世界所有的人们&#xff0c;包括哪怕是最孤立、与世隔绝的部落&#xff0c;都会有自己的特色音乐……”好吧&#xff0c;我们开源人就构成了一个部落。我建议我们的“音乐形式”…

halcon 算子shape_trans

shape_trans 函数原型&#xff1a;shape_trans(Region : RegionTrans : Type : ) 函数作用&#xff1a;变换区域的形状 参数列表&#xff1a; Region&#xff08;in&#xff09;&#xff1a;被变换的区域 RegionTrans&#xff08;out&#xff09;&#xff1a;变换后的区域…

【SmartApi】内网局域网API调试开发工具下载使用说明-v1.0.1

最近的一些年经常在内网环境下进行开发工作&#xff0c;尤其是比较敏感的单位或者组织是有严格规定不能使用哪些软件或者是不能访问外网的。 但开发工作需要的各种软件工具又因为一些营销策略而要联网激活&#xff0c;这就和组织单位的要求相悖了。于是就萌发了开发内网局域网软…

Ajax跨域访问,访问成功但一直走error不走success的的问题解决

Ajax跨域访问,访问成功但一直走error不走success的的问题解决 通过搜索各种资料&#xff0c;终于解决啦&#xff0c;废话不多说了&#xff0c;还是老规矩直接上代码&#xff1a; 我这里用了jsonp&#xff0c;有想了解的点击 : jsonp 前端代码&#xff1a; $.ajax({type:post…

无法向会话状态服务器发出会话状态请求。请确保 ASP.NET State Service (ASP.NET 状态服务)已启动,并且客户端端口与服务器端口相同

“/”应用程序中的服务器错误。 无法向会话状态服务器发出会话状态请求。请确保 ASP.NET State Service (ASP.NET 状态服务)已启动&#xff0c;并且客户端端口与服务器端口相同。如果服务器位于远程计算机上&#xff0c;请检查 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Se…

第二证券:汽车产业链股活跃,恒勃股份、博俊科技“20cm”涨停

轿车产业链股9日盘中走势活跃&#xff0c;截至发稿&#xff0c;恒勃股份、博俊科技“20cm”涨停&#xff0c;德迈仕涨超17%&#xff0c;上声电子涨超14%&#xff0c;川环科技涨超10%&#xff0c;圣龙股份、科华控股、沪光股份、上海沿浦、日盈电子、赛力斯等均涨停。 工作方面…

Linux 部署 MinIO 分布式对象存储 配置为 typora 图床

前言 MinIO 是一款高性能的对象存储系统&#xff0c;它可以用于大规模的 AI/ML、数据湖和数据库工作负载。它的 API 与Amazon S3 云存储服务完全兼容&#xff0c;可以在任何云或本地基础设施上运行。MinIO 是开源软件&#xff0c;也提供商业许可和支持 MinIO 的特点有&#x…

30分钟快速搭建并部署一个免费的个人博客

1前言 现如今网上有许多完善的博客平台&#xff0c;如博客园、掘金、思否、知乎等。有人会说为什么现在网上有这么多成熟的博客平台&#xff0c;你还要浪费时间搭建一个自己的博客系统呢&#xff1f;首先我相信每一个程序员都会想要拥有一个属于自己的博客系统&#xff0c;其次…