spring boot 版本
3.1.3
配置虚拟线程的载体线程池
如果服务器CPU核心数太多,且程序并不需要支持太多的并发量,则强烈建议配置一下,以减少使用的平台线程数。jdk源码位置:java.lang.VirtualThread#createDefaultScheduler,开放了3个配置项,对这3个选项的修改必须在加载java.lang.VirtualThread类之前!
下面通过spring集成环境来配置虚拟线程的载体线程池,但如果有特殊代码执行在ApplicationEnvironmentPreparedEvent事件之前且这些特殊代码导致了java.lang.VirtualThread类在此spring事件之前被加载,那么强烈建议修改优化这些特殊代码的执行时机,否则就只能把配置时机提前到main方法里,那就失去了spring集成能力。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @SpringBootApplication public class DemoApplication implements ApplicationListener<ApplicationEnvironmentPreparedEvent> { public static void main(String[] args) { SpringApplication application = new SpringApplication(DemoApplication.class); application.addListeners(new DemoApplication()); application.run(args); } @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); String parallelism = environment.getProperty("jdk.virtual-thread-scheduler.parallelism"); String maxPoolSize = environment.getProperty("jdk.virtual-thread-scheduler.max-pool-size"); String minRunnable = environment.getProperty("jdk.virtual-thread-scheduler.min-runnable"); if (parallelism != null) { System.setProperty("jdk.virtualThreadScheduler.parallelism", parallelism); } if (maxPoolSize != null) { System.setProperty("jdk.virtualThreadScheduler.maxPoolSize", maxPoolSize); } if (minRunnable != null) { System.setProperty("jdk.virtualThreadScheduler.minRunnable", minRunnable); } } }
|
application.yml 示例:
1 2 3 4 5
| jdk: virtual-thread-scheduler: parallelism: 4 max-pool-size: 16 min-runnable: 1
|
配置Java11新增的HttpClient
1 2 3 4 5 6 7
| public class DemoApplication { public static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() .cookieHandler(new CookieManager()) .connectTimeout(Duration.ofMillis(100)) .executor(Executors.newCachedThreadPool(Thread.ofVirtual().name("HttpClient-v", 1).factory())) .build(); }
|
tomcat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @SpringBootApplication public class DemoApplication { @Bean public TomcatProtocolHandlerCustomizer<?> tomcatProtocolHandlerCustomizer(ServerProperties serverProperties) { ServerProperties.Tomcat.Threads threads = serverProperties.getTomcat().getThreads(); ThreadFactory factory = Thread.ofVirtual().name("tomcat-v", 1).factory(); org.apache.tomcat.util.threads.TaskQueue taskqueue = new org.apache.tomcat.util.threads.TaskQueue(); org.apache.tomcat.util.threads.ThreadPoolExecutor executor = new org.apache.tomcat.util.threads.ThreadPoolExecutor( threads.getMinSpare(), threads.getMax(), 60L, TimeUnit.SECONDS, taskqueue, factory, new org.apache.tomcat.util.threads.ThreadPoolExecutor.AbortPolicy() ); taskqueue.setParent(executor); return protocolHandler -> protocolHandler.setExecutor(executor); } }
|
application.yml 示例:
1 2 3 4 5
| server: tomcat: threads: max: 21_0000_0000 min-spare: 0
|
lettuce(redis客户端)
1 2 3 4 5 6 7 8
| @SpringBootApplication public class DemoApplication { @Bean public ClientResourcesBuilderCustomizer lettuceClientResourcesCustomizer() { ThreadFactoryProvider threadFactoryProvider = poolName -> Thread.ofVirtual().name(poolName + "-v", 1).factory(); return customizer -> customizer.threadFactoryProvider(threadFactoryProvider); } }
|
HikariDataSource(数据库连接池)
1 2 3 4 5 6 7 8 9 10
| @SpringBootApplication public class DemoApplication implements InstantiationAwareBeanPostProcessor{ @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { if (bean instanceof HikariDataSource hikari) { hikari.setThreadFactory(Thread.ofVirtual().name("hikari-v", 1).factory()); } return true; } }
|
spring 定时任务
不要通过增加spring.task.scheduling.pool.size的值来达成定时任务的并发执行,因为此数量之内的线程不会被回收,而应通过使用@EnableAsync和@Async异步机制来实现。
不要将任何java.util.concurrent.Executor的类注册为spring bean,否则会导致spring自带的默认ThreadPoolTaskExecutor失效,除非将此Executor类型的spring bean的名称设为applicationTaskExecutor和taskExecutor,但这样做又会带来更多issue。
详见spring源码位置:TaskExecutionAutoConfiguration#applicationTaskExecutor。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @EnableAsync @EnableScheduling @SpringBootApplication public class DemoApplication{ @Bean public TaskSchedulerCustomizer taskSchedulerCustomizer(TaskSchedulingProperties schedulingProperties) { String threadNamePrefix = schedulingProperties.getThreadNamePrefix() + "v"; ThreadFactory factory = Thread.ofVirtual().name(threadNamePrefix, 1).factory(); return customizer -> customizer.setThreadFactory(factory); }
@Bean public TaskExecutorCustomizer taskExecutorCustomizer(TaskExecutionProperties executionProperties) { String threadNamePrefix = executionProperties.getThreadNamePrefix() + "v"; ThreadFactory factory = Thread.ofVirtual().name(threadNamePrefix, 1).factory(); return customizer -> customizer.setThreadFactory(factory); } }
|
application.yml 示例:
1 2 3 4 5 6 7 8 9 10
| spring: task: scheduling: pool: size: 1 execution: pool: core-size: 0 max-size: 2147483647 queue-capacity: 0
|
