当前位置:首页 > 科技  > 软件

Java 新技术:虚拟线程使用指南

来源: 责编: 时间:2024-01-15 09:21:07 294观看
导读虚拟线程是在 Java 21 版本中实现的一种轻量级线程。它由 JVM 进行创建以及管理。虚拟线程和传统线程(我们称之为平台线程)之间的主要区别在于,我们可以轻松地在一个 Java 程序中运行大量、甚至数百万个虚拟线程。本文是

虚拟线程是在 Java 21 版本中实现的一种轻量级线程。它由 JVM 进行创建以及管理。虚拟线程和传统线程(我们称之为平台线程)之间的主要区别在于,我们可以轻松地在一个 Java 程序中运行大量、甚至数百万个虚拟线程。6t828资讯网——每日最新资讯28at.com

6t828资讯网——每日最新资讯28at.com

本文是继《Java 21 新技术:虚拟线程使用指南》的第二篇文章,无意全面涵盖虚拟线程的每个重要细节,目的是给大家使用虚拟线程提供一套使用指南,帮助大家能更好使用的虚拟线程,发挥其作用并避免踩坑。6t828资讯网——每日最新资讯28at.com

本文完整大纲如下,6t828资讯网——每日最新资讯28at.com

6t828资讯网——每日最新资讯28at.com

使用信号量限制并发

在某些场景下,我们需要限制某个操作的并发数。例如某些外部服务可能无法同时处理超过 10 个并发请求。6t828资讯网——每日最新资讯28at.com

由于平台线程是一种宝贵的资源,通常在线程池中进行管理,因此线程池的使用对于如今的程序员相当普遍。6t828资讯网——每日最新资讯28at.com

6t828资讯网——每日最新资讯28at.com

比如上面例子要限制并发请求数,某些人会使用线程池来处理,代码如下,6t828资讯网——每日最新资讯28at.com

ExecutorService es = Executors.newFixedThreadPool(10);...Result foo() {    try {        var fut = es.submit(() -> callLimitedService());        return f.get();    } catch (...) { ... }}

上面代码示例可以确保外部服务最多只有 10 个并发请求,因为我们的线程池中只有最多 10 个线程。6t828资讯网——每日最新资讯28at.com

限制并发只是使用线程池的副产品。线程池旨在共享稀缺资源,而虚拟线程并不稀缺,因此永远不应该池化虚拟线程!6t828资讯网——每日最新资讯28at.com

使用虚拟线程时,如果要限制访问某些服务的并发请求,则应该使用专门为此目的设计的 Semaphore 类。示例代码如下,6t828资讯网——每日最新资讯28at.com

Semaphore sem = new Semaphore(10);...Result foo() {    sem.acquire();    try {        return callLimitedService();    } finally {        sem.release();    }}

在这个示例中,同一时刻只有 10 个虚拟线程可以进入 foo() 方法取得锁,而其他虚拟线程将会被阻塞。6t828资讯网——每日最新资讯28at.com

简单地使用信号量阻塞某些虚拟线程可能看起来与将任务提交到固定数量线程池有很大不同,但事实并非如此。6t828资讯网——每日最新资讯28at.com

将任务提交到等待任务池会将它们排队处理,信号量在内部(或任何其他阻塞同步构造)构造了一个阻塞线程队列,这些任务在阻塞线程队列上也会进行排队处理。6t828资讯网——每日最新资讯28at.com

6t828资讯网——每日最新资讯28at.com

我们可以将平台线程池认作是从等待任务队列中提取任务进行处理的工作人员,然后将虚拟线程视为任务本身,在任务或者线程可以执行之前将会被阻塞,但任务或者线程被阻塞时在计算机中的底层表示上实际是相同的。6t828资讯网——每日最新资讯28at.com

这里想告诉大家的就是不管是线程池的任务排队,还是信号量内部的线程阻塞,它们之间是由等效性的。在虚拟线程某些需要限制并发数场景下,直接使用信号量即可。6t828资讯网——每日最新资讯28at.com

不要在线程局部变量中缓存可重用对象

虚拟线程支持线程局部变量,就像平台线程一样。通常线程局部变量用于将一些特定于上下文的信息与当前运行的代码关联起来,例如当前事务和用户 ID。6t828资讯网——每日最新资讯28at.com

对于虚拟线程来说,使用线程局部变量是完全合理的。但是如果考虑更安全、更有效的线程局部变量,可以使用 Scoped Values。6t828资讯网——每日最新资讯28at.com

更多有关 Scoped Values 介绍,请参阅 https://docs.oracle.com/en/java/javase/21/core/scoped-values.html#GUID-9A4565C5-82AE-4F03-A476-3EAA9CDEB0F66t828资讯网——每日最新资讯28at.com

6t828资讯网——每日最新资讯28at.com

线程局部变量有一种用途与虚拟线程是不太适合的,那就是缓存可重用对象。6t828资讯网——每日最新资讯28at.com

可重用对象的创建成本通常很高,通常消耗大量内存且可变,还不是线程安全的。它们被缓存在线程局部变量中,以减少它们实例化的次数以及它们在内存中的实例数量,好处是它们可以被线程上不同时间运行的多个任务重用,减少昂贵对象创建的开销。6t828资讯网——每日最新资讯28at.com

例如 SimpleDateFormat 的实例创建成本很高,而且不是线程安全的。为了解决创建成本、线程不安全问题,通常是将此类实例缓存在 ThreadLocal 中,如下例所示:6t828资讯网——每日最新资讯28at.com

static final ThreadLocal<SimpleDateFormat> cachedFormatter =       ThreadLocal.withInitial(SimpleDateFormat::new);void foo() {  ... cachedFormatter.get().format(...); ...}

仅当线程(以及因此在线程本地缓存的昂贵对象)被多个任务共享和重用时(就像平台线程被池化时的情况一样),这种缓存才有用。许多任务在线程池中运行时可能会调用 foo,但由于池中仅包含几个线程,因此该对象只会被实例化几次(每个池线程一次)并被缓存和重用。6t828资讯网——每日最新资讯28at.com

但是虚拟线程永远不会被池化,也不会被不相关的任务重用。因为每个任务都有自己的虚拟线程,所以每次从不同任务调用 foo 都会触发新 SimpleDateFormat 的实例化。而且由于可能有大量的虚拟线程同时运行,昂贵的对象可能会消耗相当多的内存。这些结果与线程本地缓存想要实现的结果恰恰相反。6t828资讯网——每日最新资讯28at.com

对于线程局部变量缓存可重用对象的问题,没有什么好的通用替代方案,但对于 SimpleDateFormat,我们应该将其替换为 DateTimeFormatter。DateTimeFormatter 是不可变的,因此单个实例就可以由所有线程共享:6t828资讯网——每日最新资讯28at.com

static final DateTimeFormatter formatter = DateTimeFormatter….;void foo() {  ... formatter.format(...); ...}

需要注意的是,使用线程局部变量来缓存共享的昂贵对象有时是由一些异步框架在幕后完成的,其隐含的假设是这些可重用对象只会由极少数池线程使用。6t828资讯网——每日最新资讯28at.com

所以混合虚拟线程和异步框架一起使用可能不是一个好主意,对某些方法的调用可能会导致可重用对象被重复创建。6t828资讯网——每日最新资讯28at.com

避免长时间和频繁的 synchronized

当前虚拟线程实现由一个限制是,在同步块或方法内执行 synchronized 阻塞操作会导致 JDK 的虚拟线程调度程序阻塞宝贵的操作系统线程,而如果阻塞操作是在同步块或方法外完成的,则不会被阻塞。我们称这种情况为 “Pinning”。6t828资讯网——每日最新资讯28at.com

如果阻塞操作既长期又频繁,则 “Pinning” 可能会对服务器的吞吐量产生不利影响。如果阻塞操作短暂(例如内存中操作)或不频繁则可能不会产生不利影响。6t828资讯网——每日最新资讯28at.com

为了检测可能有害的 “Pinning” 实例,(JDK Flight Recorder (JFR) 在 “Pinning” 阻塞时间超过 20 毫秒时,会发出 jdk.VirtualThreadPinned 事件。6t828资讯网——每日最新资讯28at.com

或者我们可以使用系统属性 jdk.tracePinnedThreads 在线程被 “Pinning” 阻塞时发出堆栈跟踪。6t828资讯网——每日最新资讯28at.com

启动 Java 程序时添加 -Djdk.tracePinnedThreads=full 运行,会在线程被 “Pinning” 阻塞时打印完整的堆栈跟踪,突出显示本机帧和持有监视器的帧。使用 -Djdk.tracePinnedThreads=short 运行,会将输出限制为仅有问题的帧。6t828资讯网——每日最新资讯28at.com

如果这些机制检测到既长期又频繁 “Pinning” 的地方,请在这些特定地方将 synchronized 替换为 ReentrantLock。以下是长期且频繁使用 synchronized 的示例:6t828资讯网——每日最新资讯28at.com

synchronized(lockObj) {    frequentIO();}

我们可以将其替换为以下内容:6t828资讯网——每日最新资讯28at.com

lock.lock();try {    frequentIO();} finally {    lock.unlock();}

参考资料:https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html#GUID-E695A4C5-D335-4FA4-B886-FEB88C73F23E6t828资讯网——每日最新资讯28at.com

最后说两句

针对虚拟线程的使用,相信大家心里已经有了答案。在对虚拟线程需要限制并发数的场景,使用信号量即可。在虚拟线程中使用线程局部变量时要注意避免缓存昂贵的可重用对象。对于使用到 synchronized 同步块或者方法的虚拟线程,建议替换为 ReentrantLock,避免影响吞吐量。6t828资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-60959-0.htmlJava 新技术:虚拟线程使用指南

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 接手了个项目,被if..else搞懵逼了

下一篇: 十个优秀免费开源CRM项目

标签:
  • 热门焦点
  • 一加Ace2 Pro真机揭晓 钛空灰配色质感拉满

    终于,在经过了几波预热之后,一加Ace2 Pro的外观真机图在网上出现了。还是博主数码闲聊站曝光的,这次的外观设计还是延续了一加11的方案,只是细节上有了调整,例如新加入了钛空灰
  • Redmi Buds 4开箱简评:才199还有降噪 可以无脑入

    在上个月举办的Redmi Note11T Pro系列新机发布会上,除了两款手机新品之外,Redmi还带来了两款TWS真无线蓝牙耳机产品,Redmi Buds 4和Redmi Buds 4 Pro,此前我们在Redmi Note11T
  • 7月安卓手机好评榜:三星S23Ultra好评率第一

    性能榜和性价比榜之后,我们来看最后的安卓手机好评榜,数据来源安兔兔评测,收集时间2023年7月1日至7月31日,仅限国内市场。第一名:三星Galaxy S23 Ultra好评率:95.71%在即将迎来新
  • 一年经验在二线城市面试后端的经验分享

    忠告这篇文章只适合2年内工作经验、甚至没有工作经验的朋友阅读。如果你是2年以上工作经验,请果断划走,对你没啥帮助~主人公这篇文章内容来自 「升职加薪」星球星友 的投稿,坐
  • 每天一道面试题-CPU伪共享

    前言:了不起:又到了每天一到面试题的时候了!学弟,最近学习的怎么样啊 了不起学弟:最近学习的还不错,每天都在学习,每天都在进步! 了不起:那你最近学习的什么呢? 了不起学弟:最近在学习C
  • 认真聊聊东方甄选:如何告别低垂的果实

    来源:山核桃作者:财经无忌爆火一年后,俞敏洪和他的东方甄选依旧是颇受外界关心的&ldquo;网红&rdquo;。7月5日至9日,为期5天的东方甄选&ldquo;甘肃行&rdquo;首次在自有App内直播,
  • 消息称小米汽车开始筛选交付中心:需至少120个车位

    IT之家 7 月 7 日消息,日前,有微博简介为“汽车行业从业者、长三角一体化拥护者”的微博用户 @长三角行健者 发文表示,据经销商集团反馈,小米汽车目前
  • 引领旗舰级影像能力向中端机普及 OPPO K11 系列发布 1799 元起

    7月25日,OPPO正式发布K系列新品—— OPPO K11 。此次 K11 在中端手机市场长期被忽视的影像板块发力,突破性地搭载索尼 IMX890 旗舰大底主摄,支持 OIS
  • 利用职权私自解除被封帐号 Meta开除20多名员工

    11月18日消息,据外媒援引知情人士表示,过去一年时间内,Facebook母公司Meta解雇或处罚了20多名员工以及合同工,指控这些人通过内部系统以不当方式重置用户帐号,其
Top