Skip to content

[docs update] 新增 Java 21 虚拟线程部分内容 #2190

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
202 changes: 202 additions & 0 deletions docs/java/concurrent/java-concurrent-questions-01.md
Original file line number Diff line number Diff line change
Expand Up @@ -358,4 +358,206 @@ 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<Integer> 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性能,但响应式编程难以理解、调试、使用,最终又回到了同步编程,最终虚拟线程诞生。
```

<!-- @include: @article-footer.snippet.md -->