Skip to content

Commit b8c5c74

Browse files
author
JunQiu
committed
[docs update] 新增 Java 21 虚拟线程部分内容
1 parent 1962dbd commit b8c5c74

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed
Loading

Diff for: docs/java/concurrent/java-concurrent-questions-01.md

+202
Original file line numberDiff line numberDiff line change
@@ -358,4 +358,206 @@ new 一个 `Thread`,线程进入了新建状态。调用 `start()`方法,会
358358

359359
**总结:调用 `start()` 方法方可启动线程并使线程进入就绪状态,直接执行 `run()` 方法的话不会以多线程的方式执行。**
360360

361+
## Java 21 虚拟线程
362+
363+
- 虚拟线程是一种轻量级的并发编程机制,它在代码中提供了一种顺序执行的感觉,同时允许在需要时挂起和恢复执行。虚拟线程可以看作是一种用户级线程,与操作系统的线程或进程不同,它是由编程语言或库提供的,而不是由操作系统管理的。
364+
- 看下面的图大家也许更容易理解:
365+
366+
![虚拟线程、系统线程、平台线程对比](images/java-concurrent-questions-01/virtual-thread.png)
367+
368+
### 优点
369+
- 非常轻量级:可以在单个线程中创建成百上千个虚拟线程而不会导致过多的线程创建和上下文切换。
370+
- 简化异步编程: 虚拟线程可以简化异步编程,使代码更易于理解和维护。它可以将异步代码编写得更像同步代码,避免了回调地狱(Callback Hell)。
371+
- 减少资源开销: 相比于操作系统线程,虚拟线程的资源开销更小。本质上是提高了线程的执行效率,从而减少线程资源的创建和上下文切换。
372+
373+
### 缺点
374+
- 不适用于计算密集型任务: 虚拟线程适用于I/O密集型任务,但不适用于计算密集型任务,因为密集型计算始终需要CPU资源作为支持。
375+
- 依赖于语言或库的支持: 协程需要编程语言或库提供支持。不是所有编程语言都原生支持协程。比如 Java 实现的虚拟线程。
376+
377+
### 使用 Java 虚拟线程
378+
- Java 21已经正式支持虚拟线程,大家可以在官网下载使用,在使用上官方为了降低使用门槛,尽量复用原有的 Thread 类,让大家可以更加平滑的使用。
379+
- 官方提供了以下几种方式:
380+
381+
#### 使用Thread.startVirtualThread()创建
382+
383+
```java
384+
public class VirtualThreadTest {
385+
public static void main(String[] args) {
386+
CustomThread customThread = new CustomThread();
387+
Thread.startVirtualThread(customThread);
388+
}
389+
}
390+
391+
static class CustomThread implements Runnable {
392+
@Override
393+
public void run() {
394+
System.out.println("CustomThread run");
395+
}
396+
}
397+
```
398+
399+
#### 使用Thread.ofVirtual()创建
400+
401+
```java
402+
public class VirtualThreadTest {
403+
public static void main(String[] args) {
404+
CustomThread customThread = new CustomThread();
405+
// 创建不启动
406+
Thread unStarted = Thread.ofVirtual().unstarted(customThread);
407+
unStarted.start();
408+
// 创建直接启动
409+
Thread.ofVirtual().start(customThread);
410+
}
411+
}
412+
static class CustomThread implements Runnable {
413+
@Override
414+
public void run() {
415+
System.out.println("CustomThread run");
416+
}
417+
}
418+
```
419+
420+
#### 使用ThreadFactory创建
421+
422+
```java
423+
public class VirtualThreadTest {
424+
public static void main(String[] args) {
425+
CustomThread customThread = new CustomThread();
426+
ThreadFactory factory = Thread.ofVirtual().factory();
427+
Thread thread = factory.newThread(customThread);
428+
thread.start();
429+
}
430+
}
431+
432+
static class CustomThread implements Runnable {
433+
@Override
434+
public void run() {
435+
System.out.println("CustomThread run");
436+
}
437+
}
438+
```
439+
440+
#### 使用Executors.newVirtualThreadPerTaskExecutor()创建
441+
442+
```java
443+
public class VirtualThreadTest {
444+
public static void main(String[] args) {
445+
CustomThread customThread = new CustomThread();
446+
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
447+
executor.submit(customThread);
448+
}
449+
}
450+
static class CustomThread implements Runnable {
451+
@Override
452+
public void run() {
453+
System.out.println("CustomThread run");
454+
}
455+
}
456+
```
457+
458+
### 性能对比
459+
- 通过多线程和虚拟线程的方式处理相同的任务,对比创建的系统线程数和处理耗时:
460+
- 注:统计创建的系统线程中部分为后台线程(比如 GC 线程),两种场景下都一样,所以并不影响对比。
461+
462+
```java
463+
public class VirtualThreadTest {
464+
static List<Integer> list = new ArrayList<>();
465+
public static void main(String[] args) {
466+
// 开启线程 统计平台线程数
467+
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
468+
scheduledExecutorService.scheduleAtFixedRate(() -> {
469+
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
470+
ThreadInfo[] threadInfo = threadBean.dumpAllThreads(false, false);
471+
updateMaxThreadNum(threadInfo.length);
472+
}, 10, 10, TimeUnit.MILLISECONDS);
473+
474+
long start = System.currentTimeMillis();
475+
// 虚拟线程
476+
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
477+
// 使用平台线程
478+
// ExecutorService executor = Executors.newFixedThreadPool(200);
479+
for (int i = 0; i < 10000; i++) {
480+
executor.submit(() -> {
481+
try {
482+
// 线程睡眠 0.5 s,模拟业务处理
483+
TimeUnit.MILLISECONDS.sleep(500);
484+
} catch (InterruptedException ignored) {
485+
}
486+
});
487+
}
488+
executor.close();
489+
System.out.println("max:" + list.get(0) + " platform thread/os thread");
490+
System.out.printf("totalMillis:%dms\n", System.currentTimeMillis() - start);
491+
492+
493+
}
494+
// 更新创建的平台最大线程数
495+
private static void updateMaxThreadNum(int num) {
496+
if (list.isEmpty()) {
497+
list.add(num);
498+
} else {
499+
Integer integer = list.get(0);
500+
if (num > integer) {
501+
list.add(0, num);
502+
}
503+
}
504+
}
505+
}
506+
```
507+
508+
#### 请求数10000 单请求耗时 1s
509+
510+
```plain
511+
// Virtual Thread
512+
max:22 platform thread/os thread
513+
totalMillis:1806ms
514+
515+
// Platform Thread 线程数200
516+
max:209 platform thread/os thread
517+
totalMillis:50578ms
518+
519+
// Platform Thread 线程数500
520+
max:509 platform thread/os thread
521+
totalMillis:20254ms
522+
523+
// Platform Thread 线程数1000
524+
max:1009 platform thread/os thread
525+
totalMillis:10214ms
526+
527+
// Platform Thread 线程数2000
528+
max:2009 platform thread/os thread
529+
totalMillis:5358ms
530+
```
531+
532+
#### 请求数10000 单请求耗时 0.5s
533+
```plain
534+
// Virtual Thread
535+
max:22 platform thread/os thread
536+
totalMillis:1316ms
537+
538+
// Platform Thread 线程数200
539+
max:209 platform thread/os thread
540+
totalMillis:25619ms
541+
542+
// Platform Thread 线程数500
543+
max:509 platform thread/os thread
544+
totalMillis:10277ms
545+
546+
// Platform Thread 线程数1000
547+
max:1009 platform thread/os thread
548+
totalMillis:5197ms
549+
550+
// Platform Thread 线程数2000
551+
max:2009 platform thread/os thread
552+
totalMillis:2865ms
553+
```
554+
555+
- 可以看到在密集 IO 的场景下,需要创建大量的平台线程异步处理才能达到虚拟线程的处理速度。
556+
- 因此,在密集 IO 的场景,虚拟线程可以大幅提高线程的执行效率,减少线程资源的创建以及上下文切换。
557+
- 吐槽:虽然虚拟线程我很想用,但是我 Java8 有机会升级到 Java21 吗?呜呜
558+
559+
```
560+
注:有段时间 JDK 一直致力于 Reactor 响应式编程来提高Java性能,但响应式编程难以理解、调试、使用,最终又回到了同步编程,最终虚拟线程诞生。
561+
```
562+
361563
<!-- @include: @article-footer.snippet.md -->

0 commit comments

Comments
 (0)