个人浅学jdk21的virtual thread

聊聊JDK19特性之虚拟线程 | 京东云技术团队 虚拟线程(Virtual Threads)就犹如名字一样,并非传统意义上的JAVA线程。传统意义上的JAVA线程(以下称为平台线程)跟操作系统的内核线程是一一映射的关系(如图1所示)。而对于平台线程的创建和销毁所带来的开销是非常大的,所以JAVA采用线程池的方式来维护平台线程而避免线程的反复创建和销毁。然而平台线程也会占用内存、CPU资源,往往在CPU和网络连接成为系统瓶颈前,平台线程首当其冲的会成为系统瓶颈。 阅读详情

What is a Platform Thread?

A platform thread is implemented as a thin wrapper around an operating system (OS) thread. A platform thread runs Java code on its underlying OS thread, and the platform thread captures its OS thread for the platform thread's entire lifetime. Consequently, the number of available platform threads is limited to the number of OS threads.

Platform threads typically have a large thread stack and other resources that are maintained by the operating system. They are suitable for running all types of tasks but may be a limited resource.

什么是平台线程

平台线程是对操作系统线程的一个薄封装。一个平台线程将Java代码运行在它底层的操作线程上,它在其整个生命周期里都捕获着操作系统线程,因此,可用的平台线程数被限制为操作系统线程数

平台线程通常拥有一个巨大的线程栈,以及其他被操纵系统所维护的资源。它们适合执行所有类型的任务但拥有有限的资源

What is a Virtual Thread?

Like a platform thread, a virtual thread is also an instance of java.lang.Thread. However, a virtual thread isn't tied to a specific OS thread. A virtual thread still runs code on an OS thread. However, when code running in a virtual thread calls a blocking I/O operation, the Java runtime suspends the virtual thread until it can be resumed. The OS thread associated with the suspended virtual thread is now free to perform operations for other virtual threads.

Virtual threads are implemented in a similar way to virtual memory. To simulate a lot of memory, an operating system maps a large virtual address space to a limited amount of RAM. Similarly, to simulate a lot of threads, the Java runtime maps a large number of virtual threads to a small number of OS threads.

Unlike platform threads, virtual threads typically have a shallow call stack, performing as few as a single HTTP client call or a single JDBC query. Although virtual threads support thread-local variables and inheritable thread-local variables, you should carefully consider using them because a single JVM might support millions of virtual threads.

Virtual threads are suitable for running tasks that spend most of the time blocked, often waiting for I/O operations to complete. However, they aren't intended for long-running CPU-intensive operations.

什么是虚拟线程

就像一个平台线程那样,一个虚拟线程也是一个java.lang.Thread的实例,然而,一个虚拟线程并不与某个操作系统线程所绑定。一个虚拟线程仍然是在操作系统线程上运行代码,然而,当运行在虚拟线程中的代码调用了一个阻塞的IO操作时,Java运行时会将虚拟线程暂时挂起直到它可以被恢复运行,此时与被挂起的虚拟线程所关联的操作系统线程便有空去执行其他虚拟线程。

虚拟线程的实现方式有点像虚拟内存。为了模拟大量的内存,操作系统将会映射大量的虚拟地址空间到有限数量的RAM(不太看得懂,没有相关知识)。类似的,为了模拟大量的线程,Java运行时会将大量的虚拟线程映射到少数的操作系统线程上

与平台线程不同,虚拟线程有比较浅的调用栈,执行像单个http请求或者单个JDBC查询操作一样少的操作。尽管虚拟线程支持thread-local变量和可继承的thread-local变量,但是当你要使用它们的时候应该谨慎考虑,因为一个jvm也许会支持上百万个虚拟线程

虚拟线程适合执行那种大部分时间都在阻塞的,在等待IO操作完成的任务。然而,它们并不适用于长久运行的cpu密集型操作。

Why Use Virtual Threads?

Use virtual threads in high-throughput concurrent applications, especially those that consist of a great number of concurrent tasks that spend much of their time waiting. Server applications are examples of high-throughput applications because they typically handle many client requests that perform blocking I/O operations such as fetching resources.

Virtual threads are not faster threads; they do not run code any faster than platform threads. They exist to provide scale (higher throughput), not speed (lower latency).

为何使用虚拟线程

虚拟线程适合被用在高吞吐量的并发程序中,特别是那些拥有大量并发的,且通常需要阻塞等待的任务的情况下。服务器程序就是这样一个例子,吞吐量高,因为它们通常需要处理许多客户端请求,且需要执行阻塞的IO任务例如获取资源(例如查数据库?)。

虚拟线程并不比普通线程快,它们并会不比平台线程更快地执行代码,它们存在的意义是提高吞吐量,而不是速度

Creating and Running a Virtual Thread

The `Thread` and `Thread.Builder` APIs provide ways to create both platform and virtual threads. The `java.util.concurrent.Executors` class also defines methods to create an ExecutorService that starts a new virtual thread for each task.

创建并执行虚拟线程

`Thread`以及`Thread.Builder`API都提供了创建平台线程和虚拟线程的方式,`java.util.concurrent.Executors`也提供了创建能为每个提交的任务开始一个虚拟线程的`ExecutorService`的方法。

使用Thread和builder来创建虚拟线程

直接创建并执行一个虚拟线程

public static void main(String[] args) throws InterruptedException {
  var thread = Thread.ofVirtual().start(() -> System.out.println("start"));
  thread.join();
}

使用Builder接口

public static void main(String[] args) throws InterruptedException {
  var builder = Thread.ofVirtual().name("test");
  var thread = builder.start(() -> System.out.println(Thread.currentThread().getName() + " running"));
  thread.join();

  /*
    test running
   */
}

通过Builder可创建并执行多个线程

public static void main(String[] args) throws InterruptedException {
  // name(prefix: String, start: int) 第二个参数是计数器,每创建一个线程加一,线程名便是"thread-1", "thread-2"
  var builder = Thread.ofVirtual().name("thread-", 1);
  var thread1 = builder.start(() -> System.out.println(Thread.currentThread().getName() + " running"));
  var thread2 = builder.start(() -> System.out.println(Thread.currentThread().getName() + " running"));
  thread1.join();
  thread2.join();
  /* 
    thread-0 running
    thread-1 running
   */
}

虚拟线程 vs 平台线程

测试方法

从这篇文获得的启示: https://juejin.cn/post/7280746515526058038

/**
 * 用给定的ExecutorService执行任务,每个任务内阻塞1s
 * @param service ExecutorService
 * @param countDownLatch CountDownLatch
 */
static void runTasks(ExecutorService service, CountDownLatch countDownLatch, AtomicInteger counter) {
  for (int i = 0; i < 500; i++) {
    service.submit(() -> {
      try {
        // simulate blocking
        Thread.sleep(1000L);
      } catch (InterruptedException e) {
        e.printStackTrace();
      } finally {
        counter.getAndIncrement();
        countDownLatch.countDown();
      }
    });
  }
}

main方法(普通线程池,500个任务,100个线程,遇到阻塞时底层操作系统线程仍然被占有,一百个任务并行执行完之后,下一批一百个任务才能继续被并行执行,由于是并行执行,所以一批任务总共只需等待1s,分五批执行,最终耗时5s)

public static void main(String[] args) {
  var countdownLatch = new CountDownLatch(500);
  // newFixedThreadPool
  try (var service = Executors.newFixedThreadPool(100)) {
    long start = System.currentTimeMillis();
    AtomicInteger counter = new AtomicInteger();
    runTasks(service, countdownLatch, counter);
    countdownLatch.await();
    System.out.println("result: " + counter.get());
    System.out.println("用时: " + (System.currentTimeMillis() - start));
  } catch (Exception e) {
    e.printStackTrace();
  }
}

输出结果

result: 500
用时: 5059

main方法(改成虚拟线程,遇到阻塞时虚拟线程被挂起,底层的操作系统线程可以去执行其他的虚拟线程的操作,保证所有任务能够同时并行执行,只需耗时1s)

public static void main(String[] args) {
  var countdownLatch = new CountDownLatch(500);
  // newVirtualThreadPerTaskExecutor
  try (var service = Executors.newVirtualThreadPerTaskExecutor()) {
    var counter = new AtomicInteger();
    long start = System.currentTimeMillis();
    runTasks(service, countdownLatch, counter);
    countdownLatch.await();
    System.out.println("result: " + counter.get());
    System.out.println("用时: " + (System.currentTimeMillis() - start));
  } catch (Exception e) {
    e.printStackTrace();
  }
}

输出结果

result: 500
用时: 1027
Java19新特性-虚拟线程(第一次预览) 众所周知,JVM 是一个多线程环境,它通过为我们提供了对操作系统线程的抽象,但是 Java 中的线程都只是对操作系统线程的一种简单封装,我们可以称之为“平台线程”。代价昂贵:创建平台线程的成本很高。每当创建一个平台线程时,操作系统都必须在堆栈中分配大量内存来存储线程的上下文、原生调用堆栈和 Java 调用堆栈。由于堆栈大小是固定的,这就导致了高昂的内存开销。上下文切换成本高:在多线程环境下,需要在不同线程间切换,这种上下文切换会消耗时间和资源。线程数量有限。 阅读详情

相关推荐

Java 19新特性:虚拟线程(Virtual Threads )

JDK19马上就要发布了,今天我们来谈一谈JDK 19的新特性虚线程,它属于JEP 425 Project Loom(425:)。这可能是JDK 19中最值得期待的预览特性之一。我们知道Java阻塞平台线程的代价很高,这就是为什么要不惜一切代价避免这种情况,以及为什么要基于回调编写响应式代码。响应式代码做得很好,但它使代码难以阅读和测试,这显然不是理想的情况。使用DK9和虚拟线程,阻塞线程变得如此廉价,以至于我们不再需要编写响应式代码,我们 的老式阻塞同步代码也可以很好地工作!

XiumingLee的博客 6568

虚拟线程常见问题总结

虚拟线程Virtual Thread)是 JDK 而不是 OS 实现的轻量级线程(Lightweight Process,LWP),由 JVM 调度。许多虚拟线程共享同一个操作系统线程,虚拟线程的数量可以远大于操作系统线程的数量。在引入虚拟线程之前,包已经支持所谓的平台线程(Platform Thread),也就是没有虚拟线程之前,我们一直使用的线程

2401_86844317的博客 1568

JDK 19 协程新特性:Project Loom 项目的延续

探讨了协程的定义、发展史和 JDK 19 中的实现原理。协程是一种轻量级的并发编程工具,通过 Continuation 控制流、Scheduler 调度和阻塞操作的改造,实现了非阻塞、高效的并发编程。历史回顾包括协程在不同编程语言中的应用和 Java 协程的发展。JDK 19 中的协程原理深入分析了创建协程的方法、非阻塞 IO 改造、Continuation 的作用以及协程的唤醒机制。总结了协程的核心原理和重要组件。

曾经“等你生日那天”都遥远得像未来,如今却可欢愉的挥手说“下个十年见” 9万+

Java虚拟线程

自从Go凭着goroutine又带火了协程这个概念,连近亲Kotlin也有了协程,Java终于坐不住了,最新的release 19里带来了Java版协程,即虚拟线程(不过目前还只是Preview阶段,按以往的尿性判断,正式可用估计要等到起码Java 21了(不过,对于万年Java 8的我们有区别吗?写这篇文章的目的,就是梳理一下我的理解,顺便捋一捋Java线程的过去、现在和来。

qq_35462834的博客 887

【问题】jdk20执行虚拟线程报错解决方案

java: ofPlatform() 是预览 API,默认情况下处于禁用状态。(请使用 --enable-preview 以启用预览 API)编辑配置 增加jvm参数。

我是Superman丶的博客 4577

Java虚拟线程官方文档(译文)

平台线程是作为操作系统线程的瘦包装器实现的。平台线程在其底层操作系统线程上运行Java代码,并且平台线程在整个生命周期内捕获其操作系统线程。因此,可用的平台线程数量受限于操作系统线程的数量。平台线程通常有一个大的线程栈和其他由操作系统维护的资源。它们适合运行所有类型的任务,但它们可能是有限的资源。像平台线程一样,虚拟线程也是类的实例。但是,虚拟线程并不绑定到特定的操作系统线程。虚拟线程仍然是在操作系统线程上运行代码。

乐征skyline 811

SpringBoot虚拟线程

System.out.println("虚拟线程正在运行");System.out.println("主线程正在运行");效率提升最高近百分之500。

qq_74831425的博客 183

JDK19 - Virtual Thread 虚拟线程探究

先说下我们一般JDK8开发过程中使用到的线程,这部分线程我们称之为平台线程(为的就是和虚拟线程做区分)。平台线程在底层操作系统线程上运行Java代码,并在代码的整个生命周期中捕获操作系统线程。但是在JDK19当中的虚拟线程,他们则是用户模式线程。即由应用程序自己实现和管理的,而不是由操作系统内核实现和管理。

Zong_0915的博客 1674

MYSQL InnoDB 行格式揭秘:探讨数据行头信息的关键组成

在数据库管理系统中,理解数据行格式的内部结构对于优化性能和确保数据完整性至关重要。这次我们来探讨一下数据行格式,特别关注行头信息的各个属性,如delete_mask、min_rec_flag、n_owned、heap_no、record_type和next_record等。

曾经“等你生日那天”都遥远得像未来,如今却可欢愉的挥手说“下个十年见” 11万+

线程回答的滚瓜烂熟,面试官问我虚线程了解吗?我说不太了解!

虚拟线程是由 Java 虚拟机调度,它的占用空间小,同时使用轻量级的任务队列来调度虚拟线程,避免了线程间基于内核的上下文切换开销,因此可以极大量地创建和使用。主要有以下好处:虚拟线程是轻量级的,它们不直接映射到操作系统的线程,而是由JVM在用户态进行管理。这种轻量级特性允许在单个JVM实例中同时运行数百万个虚拟线程。虚拟线程大大简化了并发编程的复杂性。开发者可以像编写顺序代码一样编写并发代码,而无需担心传统线程编程中的许多复杂问题,如线程数、同步和资源竞争等。

半亩方塘立身 882

Java终于发布了“协程“--虚拟线程,原来上手这么简单!

说明:在现有的线程模型下,一个 Java 线程相当于一个操作系统线程,多个虚拟线程需要挂载在一个平台线程(载体线程)上,每个平台线程和系统线程一一对应。虚拟线程执行到 IO 操作或 Blocking 操作时,会自动切换到其他虚拟线程执行,从而避免当前线程等待,可以高效通过少数线程去调度大量虚拟线程,最大化提升线程执行效率。通过上述列举的 4 种创建虚拟线程的方式可以看出,官方为了降低虚拟线程的门槛,尽力复用原有的 Thread 线程类,这样可以平滑的过渡到虚拟线程的使用。因此,虚拟线程就孕育而生。

weixin_45131680的博客 1107

Spring Boot 中的虚拟线程

虚拟线程作为 Java 中的一项功能引入,旨在简化并发性。Virtual threads 是 轻量级的线程,由 Java Virtual Machine 而不是操作系统管理。它们被设计为易于使用且高效,为并发编程提供了比传统 Java 线程更简单的模型。Lightweight :与传统线程相比,虚拟线程的重量更轻。它们由 JVM 管理,许多虚拟线程可以映射到较少数量的操作系统线程。Concurrency :虚拟线程旨在通过更轻松地编写可扩展和响应式应用程序来简化并发编程。

xuejianxinokok的专栏 1564

虚拟线程用法

虚拟线程Java中的一种轻量级线程,由JVM管理,特别适合于I/O密集型任务。4. 使用Executors.newVirtualThreadPerTaskExecutor()创建虚拟线程。以上是虚拟线程的一些基本用法,它们可以帮助你更有效地处理高并发的I/O密集型任务。这种方式适合于需要大量并发执行任务的场景,每个任务都会在一个新的虚拟线程执行。这种方式会立即启动虚拟线程,并在其内部执行传入的Runnable任务。这种方式允许你更灵活地控制虚拟线程的启动时机。

silver687的博客 585

VirtualThread Pin的处理总结

Pin的状态指的是VirtualThread在freeze时无法让出Carrier Thread(协程执行时挂载的物理线程)。主要有两种情况下会导致Pin: VirtualThread的调用栈包含JNI frame。因为JNI调用的实现是C++代码,可以做的事情非常多,例如它可以保存当前Carrier ThreadThread ID,如果这时切换出去,那么下一次执行时,如果另一个Carrier Thread执行这个协程,将会产生逻辑错误(Carrier Thread的ID不一致); VirtualThr

流子的专栏 1002

【多线程及高并发 番外篇】虚拟线程怎么被 synchronized 阻塞了?

这系列文章讲述多线程及高并发详解,此文主要讲述虚拟线程概念、实现原理。在使用虚拟线程的过程中,synchronized 会导致虚拟线程阻塞的问题及原理分析

若明天不见 3654

JDK 21 中的虚拟线程详解

JDK 21 中,引入了一个重要的特性 —— 虚拟线程virtual thread)。虚拟线程是一种轻量级的用户模式线程,与传统 Java 平台的内核线程不同。它运行在 JVM 中,并非直接映射到操作系统的内核线程,而是由 JVM 进行调度和管理,不依赖传统操作系统的调度机制。这使得虚拟线程的创建和切换成本非常低。

m0_57836225的博客 2110

Java 21 正式 GA,虚拟线程真的来了

Java 21 正式 GA,虚拟线程真的来了

u010308917的博客 1486

Java21 虚拟线程

虚拟线程是一种轻量化的线程封装,由jvm直接调度和管理。反之普通的线程其实是调用的操作系统的能力,对应的是操作系统级的线程。相对虚拟线程来说操作系统级的线程持有成本很高,而且受操作系统调度和管理的。实际在普通多线程情况下,如果出现IO阻塞,这个线程就必须得跟着阻塞,这个线程对应的操作系统就被阻塞,而他却持有大量的内存。另外,要处理大量的IO就得新建更多线程,而大量的线程会在操作系统切换时因上下文切换导致大量的CPU被浪费。

summer_fish的专栏 2243
上一篇: 两个独立的线程,交替打印奇数偶数
下一篇: hwod机考练习
根本戒不掉滋崩
博客等级 码龄7年 4粉丝 · 3原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值