springboot3 + java虚拟线程初体验

java虚拟线程介绍

虚拟线程是 Java 19 的 预览特性,估计会在Java 21被纳入 JDK 的正式版本中,会在2023年9月发布,目前springboot 3 已经提供了对虚拟线程的支持。

虚拟线程和平台线程主要区别在于,虚拟线程在运行周期内不依赖操作系统线程:它们与硬件脱钩,因此被称为 “虚拟”。这种解耦是由 JVM 提供的抽象层赋予的。
虚拟线程的运行成本远低于平台线程。消耗的内存要少得多。这就是为什么可以创建数百万个虚拟线程而不会出现内存不足的问题,而标准平台(或内核)线程只能创建数百个。
从理论上讲,这赋予了开发人员一种超级能力:无需依赖异步代码即可实现高性能的应用程序。

预计在不久的未来,常见的开源框架(Tomcat、Spring、Netty)都会基于虚拟线程推出新版本。

环境准备

java 20:下载地址:OpenJDK JDK 20.0.2 GA Release

idea社区版2023.2版本,目前最高支持到java 20,下载地址:下载 IntelliJ IDEA – 领先的 Java 和 Kotlin IDE

新建springboot3项目

https://start.spring.io/

在idea打开项目,最终项目文件如下图。修改项目设置:

项目文件

POM文件:修改后一定要执行一次:Maven--reload project

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>20</source>
					<target>20</target>
					<compilerArgs>
						<arg>--enable-preview</arg>
					</compilerArgs>
				</configuration>
			</plugin>

springboot启动文件

@SpringBootApplication
@EnableAsync
public class VttestApplication {

	public static void main(String[] args) {
		SpringApplication.run(VttestApplication.class, args);
	}
}
ThreadConfig.java
@Configuration
@ConditionalOnProperty(value = "spring.thread-executor", havingValue = "virtual")
public class ThreadConfig {

    @Bean
    public AsyncTaskExecutor applicationTaskExecutor() {
        return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
    }

    @Bean
    public TomcatProtocolHandlerCustomizer<?> protocolHandlerCustomizer() {
        return protocolHandler -> {
            protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
        };
    }
}
AsyncService.java
@Service
public class AsyncService {
    private static final Log log = LogFactory.getLog(AsyncService.class);
    /**
     *
     * @param countDownLatch 用于测试
     */
    @Async
    public void doSomething(CountDownLatch countDownLatch) {
//        log.info(Thread.currentThread());
        try {
            Thread.sleep(50);
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        }
        countDownLatch.countDown();
    }
}
TestController.java
@RestController
@RequestMapping("/test")
public class TestController {

    private static final Log log = LogFactory.getLog(TestController.class);

    @Autowired
    AsyncService asyncService;

    @GetMapping("/vt")
    public String vt() {
        long start = System.currentTimeMillis();
        int n = 10000;
        CountDownLatch countDownLatch = new CountDownLatch(n);
        for (int i = 0; i < n; i++) {
            asyncService.doSomething(countDownLatch);
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        }

        long end = System.currentTimeMillis();
        log.info("耗时:" + (end - start) + "ms");
        return "OK";
    }

    @GetMapping("/ds")
    public void doSomething() throws InterruptedException {
//        log.info("hey, I'm doing something");
        Thread.sleep(1000);
    }

}

application.yml

server:
  port: 8092
  #开启优雅停机,默认immediate是立即关机
  shutdown: graceful
  tomcat.threads.max: ${TOMCAT_THREAD_NUM:800}


logging:
  level:
    com:
      demo:
        springboottest: DEBUG
#    ROOT: debug

spring:
  thread-executor: virtual  #启动虚拟线程的必须配置
  lifecycle:
    timeout-per-shutdown-phase: 30s #设置优雅停机缓冲期,最大等待时间

关键是:thread-executor: virtual #启动虚拟线程的必须配置

注释掉该行则使用普通线程,后面对比虚拟线程与普通线程性能时,可通过注释该行切换到普通线程

运行项目

通过接口 http://127.0.0.1:8092/test/vt 验证两种线程执行效率

虚拟线程执行时间:

普通线程执行时间:

这段代码执行时间相差1000倍

使用Jmeter压测  http://127.0.0.1:8092/test/ds  接口,吞吐量、响应时间均有大幅优化,并发数越高优化幅度越大。并发2000情况下的结果如下

虚拟线程

普通线程

参考文章:

在 Spring 6 中使用虚拟线程(Virtual Threads) - spring 中文网

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
您可以参考以下示例代码来实现Spring Boot与MyBatis集成,使用Oracle数据库进行多线程快速插入百万条数据,并保证事务的一致性。 首先,您需要在pom.xml文件中添加所需的依赖: ```xml <dependencies> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- MyBatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <!-- Oracle JDBC Driver --> <dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc8</artifactId> <version>19.8.0.0</version> </dependency> </dependencies> ``` 接下来,创建一个实体类来表示您要插入的数据: ```java public class Data { private String name; // 其他字段... // Getter和Setter方法... } ``` 然后,创建一个Mapper接口来定义数据访问的方法: ```java @Mapper public interface DataMapper { void insertData(Data data); } ``` 接着,在application.properties文件中配置数据库连接信息: ```properties spring.datasource.url=jdbc:oracle:thin:@localhost:1521/ORCL spring.datasource.username=your-username spring.datasource.password=your-password spring.datasource.driver-class-name=oracle.jdbc.OracleDriver ``` 现在,您可以编写一个Service类来执行插入操作: ```java @Service public class DataService { @Autowired private DataMapper dataMapper; @Transactional public void insertMillionData() { ExecutorService executorService = Executors.newFixedThreadPool(10); // 使用10个线程插入数据 for (int i = 0; i < 1000000; i++) { final int index = i; executorService.execute(() -> { Data data = new Data(); data.setName("Data " + index); // 设置其他字段... dataMapper.insertData(data); }); } executorService.shutdown(); try { executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } ``` 最后,在启动类中添加@SpringBootApplication注解,并在main方法中调用DataService的insertMillionData方法: ```java @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); // 调用插入数据的方法 ApplicationContext context = SpringApplication.run(Application.class, args); DataService dataService = context.getBean(DataService.class); dataService.insertMillionData(); } } ``` 以上示例代码演示了如何使用Spring Boot和MyBatis实现多线程快速插入百万条数据,并保证事务的一致性。您可以根据实际需求进行适当的修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值