Spring Boot 中的虚拟线程

在本文中,我将讨论 Spring Boot 中的虚拟线程。

什么是虚拟线程?

虚拟线程作为 Java 中的一项功能引入,旨在简化并发性。 Virtual threads 是 轻量级的线程,由 Java Virtual Machine 而不是操作系统管理。它们被设计为易于使用且高效,为并发编程提供了比传统 Java 线程更简单的模型。

  • Lightweight :与传统线程相比,虚拟线程的重量更轻。它们由 JVM 管理,许多虚拟线程可以映射到较少数量的操作系统线程。
  • Concurrency :虚拟线程旨在通过更轻松地编写可扩展和响应式应用程序来简化并发编程。
  • Thread Pool :不需要显式管理线程池。 JVM 可以根据工作负载动态调整线程数量。

没有执行器的虚拟线程声明方式

java 复制代码
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

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

yaml 复制代码
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

1、创建100_000个传统Java线程并执行。
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

Duration Time(Milliseconds): 18648

CPU使用率:

2.、创建100_000个虚拟Java线程并执行。
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

Duration Time(Milliseconds): 3749

CPU使用率:

3. 创建Http控制器
java 复制代码
@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";
    }

}
3.1 发送HTTP请求(传统线程)

发送 1600 请求。

并发请求数:400

命令

shell 复制代码
ab -n 1600 -c 400 host.docker.internal:8080/api/v1/threads

Response Summary

复制代码
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking host.docker.internal (be patient)
Completed 160 requests
Completed 320 requests
Completed 480 requests
Completed 640 requests
Completed 800 requests
Completed 960 requests
Completed 1120 requests
Completed 1280 requests
Completed 1440 requests
Completed 1600 requests
Finished 1600 requests


Server Software:
Server Hostname:        host.docker.internal
Server Port:            8080

Document Path:          /api/v1/threads
Document Length:        15 bytes

Concurrency Level:      400
Time taken for tests:   9.659 seconds
Complete requests:      1600
Failed requests:        0
Total transferred:      236800 bytes
HTML transferred:       24000 bytes
Requests per second:    165.65 [#/sec] (mean)
Time per request:       2414.722 [ms] (mean)
Time per request:       6.037 [ms] (mean, across all concurrent requests)
Transfer rate:          23.94 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        2  127  51.4    118     375
Processing:  1040 1877 258.2   1905    2438
Waiting:     1010 1755 257.0   1781    2304
Total:       1042 2004 268.9   2019    2660

Percentage of the requests served within a certain time (ms)
  50%   2019
  66%   2032
  75%   2041
  80%   2048
  90%   2540
  95%   2603
  98%   2638
  99%   2650
 100%   2660 (longest request)

Time taken for tests :9.659 秒

3.2发送HTTP请求(虚拟线程)

创建线程执行器

线程执行器配置

java 复制代码
@Configuration
@Slf4j
public class ThreadExecutorConfig {

    @Bean
    public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
        return protocolHandler -> {
            log.info("Configuring " + protocolHandler + " to use VirtualThreadPerTaskExecutor");
            protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
        };
    }

}

发送 1600 请求。

并发请求数:400

Command 命令

复制代码
ab -n 1600 -c 400 host.docker.internal:8080/api/v1/threads

Response Summary

复制代码
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking host.docker.internal (be patient)
Completed 160 requests
Completed 320 requests
Completed 480 requests
Completed 640 requests
Completed 800 requests
Completed 960 requests
Completed 1120 requests
Completed 1280 requests
Completed 1440 requests
Completed 1600 requests
Finished 1600 requests


Server Software:
Server Hostname:        host.docker.internal
Server Port:            8080

Document Path:          /api/v1/threads
Document Length:        0 bytes

Concurrency Level:      400
Time taken for tests:   7.912 seconds
Complete requests:      1600
Failed requests:        0
Total transferred:      211200 bytes
HTML transferred:       0 bytes
Requests per second:    202.22 [#/sec] (mean)
Time per request:       1978.077 [ms] (mean)
Time per request:       4.945 [ms] (mean, across all concurrent requests)
Transfer rate:          26.07 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1  186  89.6    197     337
Processing:  1005 1376 251.1   1423    1855
Waiting:     1005 1198 158.2   1171    1591
Total:       1040 1562 244.2   1612    2060

Percentage of the requests served within a certain time (ms)
  50%   1612
  66%   1668
  75%   1691
  80%   1724
  90%   1903
  95%   1997
  98%   2037
  99%   2048
 100%   2060 (longest request)

Time taken for tests :7.912 秒

相关文档:

相关推荐
步行cgn28 分钟前
@ResponseBody 详解:Spring MVC 中把返回值直接写入响应体
java·spring·mvc
ServBay40 分钟前
本地模型已经跑起来了,Copilot Fusion 为什么还要联网?
后端·ai编程·github copilot
武子康1 小时前
H3-World 的动作没按时发生,先检查时间线变成了什么
人工智能·后端·agent
苍何1 小时前
腾讯 Buddy One 来了,办公全家桶一键打通WorkBuddy(附教程)
后端
jason成都1 小时前
RTKLIB 的 Java 移植版来了:rtklib-java v3.3.0 功能全景
java·gnss·rtklib·rtklib_java
LazyHunter91 小时前
JDK 28:Java 下一步方向解析与新特性展望
java·jdk
m0_639906942 小时前
Java 重写 Override 与重载 Overload 区别、访问修饰符、static 关键字
java·开发语言
GIS数据转换器2 小时前
基于CIM的城市智慧供热监管服务平台
java·大数据·开发语言·人工智能·后端·物联网·struts
adfreedom2 小时前
如何避免每个业务系统中都有几十个枚举查询
后端
m0_639906942 小时前
SpringBoot自动配置原理
java·spring boot·后端