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 秒

相关文档:

相关推荐
小雅痞24 分钟前
[Java][Leetcode middle] 167. 两数之和 II - 输入有序数组
java·算法·leetcode
CN-Dust27 分钟前
【C++】输入cin例题专题
java·c++·算法
xin_nai1 小时前
LeetCode热题100(Java)(6)矩阵
java·leetcode·矩阵
代码AI弗森7 小时前
一文理清楚“算力申请 / 成本测算 / 并发评估”
java·服务器·数据库
Java开发的小李7 小时前
SpringBoot + Redis 实现分布式 Session 共享(解决多实例登录状态丢失问题)
spring boot·redis·分布式
Old Uncle Tom7 小时前
OpenClaw 记忆系统 -- 记忆预加载
java·数据结构·算法·agent
小小小米粒7 小时前
Collection单列集合、Map(Key - Value)双列集合,多继承实现。
java·开发语言·windows
前端一小卒8 小时前
我用 Claude Code 的 Superpowers 技能链写了个服务,部署前差点把服务器搞炸
前端·javascript·后端
摇滚侠8 小时前
expdp 查看帮助
java·数据库·oracle
:1218 小时前
java基础
java·开发语言