本文讨论 Spring Boot 中的虚拟线程。
虚拟线程作为 Java 中的一项功能引入,旨在简化并发性。Virtual threads是由操作系统而不是操作系统lightweight 管理的。Java Virtual Machine它们被设计为易于使用且高效,为并发编程提供了比传统 Java 线程更简单的模型。
- Lightweight:与传统线程相比,虚拟线程的重量更轻。它们由 JVM 管理,许多虚拟线程可以映射到较少数量的操作系统线程。
- Concurrency:虚拟线程旨在通过更轻松地编写可扩展和响应式应用程序来简化并发编程。
- Thread Pool:不需要显式管理线程池。JVM 可以根据工作负载动态调整线程数量。
没有执行器的虚拟线程声明方式
public static void main(String[] args) {
Thread virtualThread = Thread.ofVirtual().start(() -> { System.out.println("Virtual thread running"); });
System.out.println("Main thread running");
} private static void main(String[] args) { Thread virtualThread = Thread.ofVirtual() .name("Virtual Thread") .unstarted(() ->System.out.println("Virtual thread running")); t.start(); try { virtualThread.join(); } catch (InterruptedException e) { e.printStackTrace(); }
}
|
Spring Boot实现
- Java Version: 20
- Spring Version: 3.1.0
1) pom.xml
<dependencies>
<!--Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
<!--Prometheus, Zipkin & Micrometer--> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> <scope>runtime</scope> <version>1.11.0</version> </dependency>
<dependency> <groupId>io.zipkin.reporter2</groupId> <artifactId>zipkin-reporter-brave</artifactId> <version>2.16.3</version> </dependency>
<dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-tracing-bridge-brave</artifactId> </dependency>
<!--Actuator & AOP--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>3.1.0</version> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
</dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>20</source> <target>20</target> <compilerArgs> --enable-preview </compilerArgs> </configuration> </plugin> </plugins> </build>
|
确保至少有 Java 21 的 JVM 可用!(如果运行的是 Java 19,则可以使用 --preview-enabled=true 运行虚拟线程)。
2) application.properties
spring.application.name=spring-vthread-service
management.zipkin.tracing.endpoint=http://${ZIPKIN_HOST:localhost}:9411/api/v2/spans management.tracing.sampling.probability=1.0 management.endpoints.web.exposure.include=info,health,prometheus,metrics server.tomcat.mbeanregistry.enabled=true management.metrics.tags.application=${spring.application.name}
logging.pattern.level=%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]
|
3) VThreadServiceApplication
创建 100_000 个传统 Java 线程并执行:
@SpringBootApplication public class VThreadServiceApplication {
public static void main(String[] args) { SpringApplication.run(VThreadServiceApplication.class, args); }
@Bean public ApplicationRunner runner() {
return args -> { var startDate = Instant.now();
startThreads();
var finishDate = Instant.now(); System.out.println(String.format("Start Date: %s, Finish Date: %s", startDate, finishDate)); System.out.println(String.format("Duration Time(Milliseconds): %s", Duration.between(startDate, finishDate).toMillis()));
}; }
private void startThreads() throws InterruptedException {
for (int i = 0; i < 100_000; i++) { int finalI = i; Thread t = new Thread(() -> System.out.println(finalI)); t.start(); t.join(); } }
}
|
输出:
.
99998 99999 Start Date: 2023-11-18T12:20:09.491114200Z, Finish Date: 2023-11-18T12:20:28.139291800Z Duration Time(Milliseconds): 18648
|
持续时间(毫秒):18648
创建 100_000 个虚拟 Java 线程并执行:
@SpringBootApplication public class VThreadServiceApplication {
public static void main(String[] args) { SpringApplication.run(VThreadServiceApplication.class, args); }
@Bean public ApplicationRunner runner() {
return args -> { var startDate = Instant.now();
startVirtualThreads();
var finishDate = Instant.now(); System.out.println(String.format("Start Date: %s, Finish Date: %s", startDate, finishDate)); System.out.println(String.format("Duration Time(Milliseconds): %s", Duration.between(startDate, finishDate).toMillis()));
}; }
private void startVirtualThreads() throws InterruptedException {
for (int i = 0; i < 100_000; i++) { int finalI = i; Thread t = Thread.ofVirtual() .name(String.format("virtualThread-%s", i)) .unstarted(() -> System.out.println(finalI)); t.start(); t.join();
} }
}
|
输出:
99998 99999 Start Date: 2023-11-18T12:22:14.838308900Z, Finish Date: 2023-11-18T12:22:18.588181800Z Duration Time(Milliseconds): 3749
|
持续时间(毫秒):3749
创建控制器:
@RestController @RequestMapping("/api/v1/threads") @Slf4j public class ThreadController {
@GetMapping("") public String thread() throws InterruptedException { Thread.sleep(1000); var threadName = Thread.currentThread().toString(); log.info(threadName); return "thread executed"; }
}
|
发送 HTTP 请求(传统线程)
- 发送 1600 个请求。
- 并发请求数:400
- 测试耗时9.659 秒
发送 HTTP 请求(虚拟线程)
创建线程执行器
@Configuration @Slf4j public class ThreadExecutorConfig {
@Bean public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() { return protocolHandler -> { log.info("Configuring " + protocolHandler + " to use VirtualThreadPerTaskExecutor"); protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor()); }; }
}
|
- 发送 1600 个请求。
- 并发请求数:400
- 测试耗时7.912 秒
源码