diff --git a/docs/.vuepress/sidebar/index.ts b/docs/.vuepress/sidebar/index.ts index 39397643..92590b2b 100644 --- a/docs/.vuepress/sidebar/index.ts +++ b/docs/.vuepress/sidebar/index.ts @@ -120,6 +120,7 @@ export default sidebar({ "atomic-classes", "threadlocal", "completablefuture-intro", + "virtual-thread", ], }, ], diff --git a/docs/java/concurrent/images/java-concurrent-questions-01/virtual-thread.png b/docs/java/concurrent/images/java-concurrent-questions-01/virtual-thread.png deleted file mode 100644 index 59ce01ae..00000000 Binary files a/docs/java/concurrent/images/java-concurrent-questions-01/virtual-thread.png and /dev/null differ diff --git a/docs/java/concurrent/java-concurrent-questions-01.md b/docs/java/concurrent/java-concurrent-questions-01.md index 079d1c8f..d5fd6d11 100644 --- a/docs/java/concurrent/java-concurrent-questions-01.md +++ b/docs/java/concurrent/java-concurrent-questions-01.md @@ -358,206 +358,4 @@ new 一个 `Thread`,线程进入了新建状态。调用 `start()`方法,会 **总结:调用 `start()` 方法方可启动线程并使线程进入就绪状态,直接执行 `run()` 方法的话不会以多线程的方式执行。** -## Java 21 虚拟线程 - -- 虚拟线程是一种轻量级的并发编程机制,它在代码中提供了一种顺序执行的感觉,同时允许在需要时挂起和恢复执行。虚拟线程可以看作是一种用户级线程,与操作系统的线程或进程不同,它是由编程语言或库提供的,而不是由操作系统管理的。 -- 看下面的图大家也许更容易理解: - -![虚拟线程、系统线程、平台线程对比](images/java-concurrent-questions-01/virtual-thread.png) - -### 优点 -- 非常轻量级:可以在单个线程中创建成百上千个虚拟线程而不会导致过多的线程创建和上下文切换。 -- 简化异步编程: 虚拟线程可以简化异步编程,使代码更易于理解和维护。它可以将异步代码编写得更像同步代码,避免了回调地狱(Callback Hell)。 -- 减少资源开销: 相比于操作系统线程,虚拟线程的资源开销更小。本质上是提高了线程的执行效率,从而减少线程资源的创建和上下文切换。 - -### 缺点 -- 不适用于计算密集型任务: 虚拟线程适用于I/O密集型任务,但不适用于计算密集型任务,因为密集型计算始终需要CPU资源作为支持。 -- 依赖于语言或库的支持: 协程需要编程语言或库提供支持。不是所有编程语言都原生支持协程。比如 Java 实现的虚拟线程。 - -### 使用 Java 虚拟线程 -- Java 21已经正式支持虚拟线程,大家可以在官网下载使用,在使用上官方为了降低使用门槛,尽量复用原有的 Thread 类,让大家可以更加平滑的使用。 -- 官方提供了以下几种方式: - -#### 使用Thread.startVirtualThread()创建 - -```java -public class VirtualThreadTest { - public static void main(String[] args) { - CustomThread customThread = new CustomThread(); - Thread.startVirtualThread(customThread); - } -} - -static class CustomThread implements Runnable { - @Override - public void run() { - System.out.println("CustomThread run"); - } -} -``` - -#### 使用Thread.ofVirtual()创建 - -```java -public class VirtualThreadTest { - public static void main(String[] args) { - CustomThread customThread = new CustomThread(); - // 创建不启动 - Thread unStarted = Thread.ofVirtual().unstarted(customThread); - unStarted.start(); - // 创建直接启动 - Thread.ofVirtual().start(customThread); - } -} -static class CustomThread implements Runnable { - @Override - public void run() { - System.out.println("CustomThread run"); - } -} -``` - -#### 使用ThreadFactory创建 - -```java -public class VirtualThreadTest { - public static void main(String[] args) { - CustomThread customThread = new CustomThread(); - ThreadFactory factory = Thread.ofVirtual().factory(); - Thread thread = factory.newThread(customThread); - thread.start(); - } -} - -static class CustomThread implements Runnable { - @Override - public void run() { - System.out.println("CustomThread run"); - } -} -``` - -#### 使用Executors.newVirtualThreadPerTaskExecutor()创建 - -```java -public class VirtualThreadTest { - public static void main(String[] args) { - CustomThread customThread = new CustomThread(); - ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); - executor.submit(customThread); - } -} -static class CustomThread implements Runnable { - @Override - public void run() { - System.out.println("CustomThread run"); - } -} -``` - -### 性能对比 -- 通过多线程和虚拟线程的方式处理相同的任务,对比创建的系统线程数和处理耗时: -- 注:统计创建的系统线程中部分为后台线程(比如 GC 线程),两种场景下都一样,所以并不影响对比。 - -```java -public class VirtualThreadTest { - static List list = new ArrayList<>(); - public static void main(String[] args) { - // 开启线程 统计平台线程数 - ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); - scheduledExecutorService.scheduleAtFixedRate(() -> { - ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); - ThreadInfo[] threadInfo = threadBean.dumpAllThreads(false, false); - updateMaxThreadNum(threadInfo.length); - }, 10, 10, TimeUnit.MILLISECONDS); - - long start = System.currentTimeMillis(); - // 虚拟线程 - ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); - // 使用平台线程 - // ExecutorService executor = Executors.newFixedThreadPool(200); - for (int i = 0; i < 10000; i++) { - executor.submit(() -> { - try { - // 线程睡眠 0.5 s,模拟业务处理 - TimeUnit.MILLISECONDS.sleep(500); - } catch (InterruptedException ignored) { - } - }); - } - executor.close(); - System.out.println("max:" + list.get(0) + " platform thread/os thread"); - System.out.printf("totalMillis:%dms\n", System.currentTimeMillis() - start); - - - } - // 更新创建的平台最大线程数 - private static void updateMaxThreadNum(int num) { - if (list.isEmpty()) { - list.add(num); - } else { - Integer integer = list.get(0); - if (num > integer) { - list.add(0, num); - } - } - } -} -``` - -#### 请求数10000 单请求耗时 1s - -```plain -// Virtual Thread -max:22 platform thread/os thread -totalMillis:1806ms - -// Platform Thread 线程数200 -max:209 platform thread/os thread -totalMillis:50578ms - -// Platform Thread 线程数500 -max:509 platform thread/os thread -totalMillis:20254ms - -// Platform Thread 线程数1000 -max:1009 platform thread/os thread -totalMillis:10214ms - -// Platform Thread 线程数2000 -max:2009 platform thread/os thread -totalMillis:5358ms -``` - -#### 请求数10000 单请求耗时 0.5s -```plain -// Virtual Thread -max:22 platform thread/os thread -totalMillis:1316ms - -// Platform Thread 线程数200 -max:209 platform thread/os thread -totalMillis:25619ms - -// Platform Thread 线程数500 -max:509 platform thread/os thread -totalMillis:10277ms - -// Platform Thread 线程数1000 -max:1009 platform thread/os thread -totalMillis:5197ms - -// Platform Thread 线程数2000 -max:2009 platform thread/os thread -totalMillis:2865ms -``` - -- 可以看到在密集 IO 的场景下,需要创建大量的平台线程异步处理才能达到虚拟线程的处理速度。 -- 因此,在密集 IO 的场景,虚拟线程可以大幅提高线程的执行效率,减少线程资源的创建以及上下文切换。 -- 吐槽:虽然虚拟线程我很想用,但是我 Java8 有机会升级到 Java21 吗?呜呜 - -``` -注:有段时间 JDK 一直致力于 Reactor 响应式编程来提高Java性能,但响应式编程难以理解、调试、使用,最终又回到了同步编程,最终虚拟线程诞生。 -``` - \ No newline at end of file diff --git a/docs/java/concurrent/java-concurrent-questions-03.md b/docs/java/concurrent/java-concurrent-questions-03.md index 16e2c74c..91e95a60 100644 --- a/docs/java/concurrent/java-concurrent-questions-03.md +++ b/docs/java/concurrent/java-concurrent-questions-03.md @@ -974,6 +974,12 @@ public int await() throws InterruptedException, BrokenBarrierException { } ``` +## 虚拟线程 + +虚拟线程在 Java 21 正式发布,这是一项重量级的更新。 + +虽然目前面试中问的不多,但还是建议大家去简单了解一下,具体可以阅读这篇文章:[虚拟线程极简入门](./virtual-thread.md) 。重点搞清楚虚拟线程和平台线程的关系以及虚拟线程的优势即可。 + ## 参考 - 《深入理解 Java 虚拟机》 diff --git a/docs/java/concurrent/virtual-thread.md b/docs/java/concurrent/virtual-thread.md new file mode 100644 index 00000000..7489828c --- /dev/null +++ b/docs/java/concurrent/virtual-thread.md @@ -0,0 +1,230 @@ +--- +title: 虚拟线程极简入门 +category: Java +tag: + - Java并发 +--- + +> 本文部分内容来自 [Lorin](https://github.com/Lorin-github) 的[PR](https://github.com/Snailclimb/JavaGuide/pull/2190)。 + +虚拟线程在 Java 21 正式发布,这是一项重量级的更新。 + +## 什么是虚拟线程? + +虚拟线程(Virtual Thread)是 JDK 而不是 OS 实现的轻量级线程(Lightweight Process,LWP),由 JVM 调度。许多虚拟线程共享同一个操作系统线程,虚拟线程的数量可以远大于操作系统线程的数量。 + +## 虚拟线程和平台线程有什么关系? + +在引入虚拟线程之前,`java.lang.Thread` 包已经支持所谓的平台线程(Platform Thread),也就是没有虚拟线程之前,我们一直使用的线程。JVM 调度程序通过平台线程(载体线程)来管理虚拟线程,一个平台线程可以在不同的时间执行不同的虚拟线程(多个虚拟线程挂载在一个平台线程上),当虚拟线程被阻塞或等待时,平台线程可以切换到执行另一个虚拟线程。 + +虚拟线程、平台线程和系统内核线程的关系图如下所示(图源:[How to Use Java 19 Virtual Threads](https://medium.com/javarevisited/how-to-use-java-19-virtual-threads-c16a32bad5f7)): + +![虚拟线程、平台线程和系统内核线程的关系](https://oss.javaguide.cn/github/javaguide/java/new-features/virtual-threads-platform-threads-kernel-threads-relationship.png) + +关于平台线程和系统内核线程的对应关系多提一点:在 Windows 和 Linux 等主流操作系统中,Java 线程采用的是一对一的线程模型,也就是一个平台线程对应一个系统内核线程。Solaris 系统是一个特例,HotSpot VM 在 Solaris 上支持多对多和一对一。具体可以参考 R 大的回答: [JVM 中的线程模型是用户级的么?](https://www.zhihu.com/question/23096638/answer/29617153)。 + +## 虚拟线程有什么优点和缺点? + +### 优点 +- 非常轻量级:可以在单个线程中创建成百上千个虚拟线程而不会导致过多的线程创建和上下文切换。 +- 简化异步编程: 虚拟线程可以简化异步编程,使代码更易于理解和维护。它可以将异步代码编写得更像同步代码,避免了回调地狱(Callback Hell)。 +- 减少资源开销: 相比于操作系统线程,虚拟线程的资源开销更小。本质上是提高了线程的执行效率,从而减少线程资源的创建和上下文切换。 + +### 缺点 +- 不适用于计算密集型任务: 虚拟线程适用于 I/O 密集型任务,但不适用于计算密集型任务,因为密集型计算始终需要 CPU 资源作为支持。 +- 依赖于语言或库的支持: 协程需要编程语言或库提供支持。不是所有编程语言都原生支持协程。比如 Java 实现的虚拟线程。 + +## 四种创建虚拟线程的方法 + +Java 21 已经正式支持虚拟线程,大家可以在官网下载使用,在使用上官方为了降低使用门槛,尽量复用原有的 `Thread` 类,让大家可以更加平滑的使用。 + +官方提供了以下四种方式创建虚拟线程: + +1. 使用 `Thread.startVirtualThread()` 创建 +2. 使用 `Thread.ofVirtual()` 创建 +3. 使用 `ThreadFactory` 创建 + + +#### 使用 Thread.startVirtualThread()创建 + +```java +public class VirtualThreadTest { + public static void main(String[] args) { + CustomThread customThread = new CustomThread(); + Thread.startVirtualThread(customThread); + } +} + +static class CustomThread implements Runnable { + @Override + public void run() { + System.out.println("CustomThread run"); + } +} +``` + +#### 使用 Thread.ofVirtual()创建 + +```java +public class VirtualThreadTest { + public static void main(String[] args) { + CustomThread customThread = new CustomThread(); + // 创建不启动 + Thread unStarted = Thread.ofVirtual().unstarted(customThread); + unStarted.start(); + // 创建直接启动 + Thread.ofVirtual().start(customThread); + } +} +static class CustomThread implements Runnable { + @Override + public void run() { + System.out.println("CustomThread run"); + } +} +``` + +#### 使用 ThreadFactory 创建 + +```java +public class VirtualThreadTest { + public static void main(String[] args) { + CustomThread customThread = new CustomThread(); + ThreadFactory factory = Thread.ofVirtual().factory(); + Thread thread = factory.newThread(customThread); + thread.start(); + } +} + +static class CustomThread implements Runnable { + @Override + public void run() { + System.out.println("CustomThread run"); + } +} +``` + +#### 使用 Executors.newVirtualThreadPerTaskExecutor()创建 + +```java +public class VirtualThreadTest { + public static void main(String[] args) { + CustomThread customThread = new CustomThread(); + ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); + executor.submit(customThread); + } +} +static class CustomThread implements Runnable { + @Override + public void run() { + System.out.println("CustomThread run"); + } +} +``` + +## 虚拟线程和平台线程性能对比 +通过多线程和虚拟线程的方式处理相同的任务,对比创建的系统线程数和处理耗时。 + +**说明**:统计创建的系统线程中部分为后台线程(比如 GC 线程),两种场景下都一样,所以并不影响对比。 + +**测试代码**: + +```java +public class VirtualThreadTest { + static List list = new ArrayList<>(); + public static void main(String[] args) { + // 开启线程 统计平台线程数 + ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); + scheduledExecutorService.scheduleAtFixedRate(() -> { + ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); + ThreadInfo[] threadInfo = threadBean.dumpAllThreads(false, false); + updateMaxThreadNum(threadInfo.length); + }, 10, 10, TimeUnit.MILLISECONDS); + + long start = System.currentTimeMillis(); + // 虚拟线程 + ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); + // 使用平台线程 + // ExecutorService executor = Executors.newFixedThreadPool(200); + for (int i = 0; i < 10000; i++) { + executor.submit(() -> { + try { + // 线程睡眠 0.5 s,模拟业务处理 + TimeUnit.MILLISECONDS.sleep(500); + } catch (InterruptedException ignored) { + } + }); + } + executor.close(); + System.out.println("max:" + list.get(0) + " platform thread/os thread"); + System.out.printf("totalMillis:%dms\n", System.currentTimeMillis() - start); + + + } + // 更新创建的平台最大线程数 + private static void updateMaxThreadNum(int num) { + if (list.isEmpty()) { + list.add(num); + } else { + Integer integer = list.get(0); + if (num > integer) { + list.add(0, num); + } + } + } +} +``` + +**请求数 10000 单请求耗时 1s**: + +```plain +// Virtual Thread +max:22 platform thread/os thread +totalMillis:1806ms + +// Platform Thread 线程数200 +max:209 platform thread/os thread +totalMillis:50578ms + +// Platform Thread 线程数500 +max:509 platform thread/os thread +totalMillis:20254ms + +// Platform Thread 线程数1000 +max:1009 platform thread/os thread +totalMillis:10214ms + +// Platform Thread 线程数2000 +max:2009 platform thread/os thread +totalMillis:5358ms +``` + +**请求数 10000 单请求耗时 0.5s**: + +```plain +// Virtual Thread +max:22 platform thread/os thread +totalMillis:1316ms + +// Platform Thread 线程数200 +max:209 platform thread/os thread +totalMillis:25619ms + +// Platform Thread 线程数500 +max:509 platform thread/os thread +totalMillis:10277ms + +// Platform Thread 线程数1000 +max:1009 platform thread/os thread +totalMillis:5197ms + +// Platform Thread 线程数2000 +max:2009 platform thread/os thread +totalMillis:2865ms +``` + +- 可以看到在密集 IO 的场景下,需要创建大量的平台线程异步处理才能达到虚拟线程的处理速度。 +- 因此,在密集 IO 的场景,虚拟线程可以大幅提高线程的执行效率,减少线程资源的创建以及上下文切换。 +- 吐槽:虽然虚拟线程我很想用,但是我 Java8 有机会升级到 Java21 吗?呜呜 + +**注意**:有段时间 JDK 一直致力于 Reactor 响应式编程来提高 Java 性能,但响应式编程难以理解、调试、使用,最终又回到了同步编程,最终虚拟线程诞生。 \ No newline at end of file diff --git a/docs/java/new-features/java20.md b/docs/java/new-features/java20.md index aca71d16..f78ce8c3 100644 --- a/docs/java/new-features/java20.md +++ b/docs/java/new-features/java20.md @@ -254,7 +254,6 @@ executorService.submit(() -> { // your code here }); -// class CustomThread implements Runnable { @Override public void run() {