diff --git a/gradle.properties b/gradle.properties index 5a0c65ece6c7..4ca6e8649f16 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,7 +19,7 @@ mavenVersion=3.9.4 mockitoVersion=5.14.2 nativeBuildToolsVersion=0.10.3 snakeYamlVersion=2.3 -springFrameworkVersion=6.2.0-RC3 +springFrameworkVersion=6.2.0-SNAPSHOT springFramework60xVersion=6.0.23 tomcatVersion=10.1.31 diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java index ee2bbf14e843..c8b9cc9d4850 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java @@ -69,12 +69,36 @@ ThreadPoolTaskExecutor applicationTaskExecutor(ThreadPoolTaskExecutorBuilder thr @Configuration(proxyBeanMethods = false) static class ThreadPoolTaskExecutorBuilderConfiguration { - @Bean - @ConditionalOnMissingBean - ThreadPoolTaskExecutorBuilder threadPoolTaskExecutorBuilder(TaskExecutionProperties properties, + private final TaskExecutionProperties properties; + + private final ObjectProvider threadPoolTaskExecutorCustomizers; + + private final ObjectProvider taskDecorator; + + ThreadPoolTaskExecutorBuilderConfiguration(TaskExecutionProperties properties, ObjectProvider threadPoolTaskExecutorCustomizers, ObjectProvider taskDecorator) { - TaskExecutionProperties.Pool pool = properties.getPool(); + this.properties = properties; + this.threadPoolTaskExecutorCustomizers = threadPoolTaskExecutorCustomizers; + this.taskDecorator = taskDecorator; + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnThreading(Threading.PLATFORM) + ThreadPoolTaskExecutorBuilder threadPoolTaskExecutorBuilder() { + return builder(); + } + + @Bean(name = "threadPoolTaskExecutorBuilder") + @ConditionalOnMissingBean + @ConditionalOnThreading(Threading.VIRTUAL) + ThreadPoolTaskExecutorBuilder threadPoolTaskExecutorBuilderVirtualThreads() { + return builder().virtualThreads(true); + } + + private ThreadPoolTaskExecutorBuilder builder() { + TaskExecutionProperties.Pool pool = this.properties.getPool(); ThreadPoolTaskExecutorBuilder builder = new ThreadPoolTaskExecutorBuilder(); builder = builder.queueCapacity(pool.getQueueCapacity()); builder = builder.corePoolSize(pool.getCoreSize()); @@ -82,12 +106,12 @@ ThreadPoolTaskExecutorBuilder threadPoolTaskExecutorBuilder(TaskExecutionPropert builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout()); builder = builder.keepAlive(pool.getKeepAlive()); builder = builder.acceptTasksAfterContextClose(pool.getShutdown().isAcceptTasksAfterContextClose()); - TaskExecutionProperties.Shutdown shutdown = properties.getShutdown(); + TaskExecutionProperties.Shutdown shutdown = this.properties.getShutdown(); builder = builder.awaitTermination(shutdown.isAwaitTermination()); builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod()); - builder = builder.threadNamePrefix(properties.getThreadNamePrefix()); - builder = builder.customizers(threadPoolTaskExecutorCustomizers.orderedStream()::iterator); - builder = builder.taskDecorator(taskDecorator.getIfUnique()); + builder = builder.threadNamePrefix(this.properties.getThreadNamePrefix()); + builder = builder.customizers(this.threadPoolTaskExecutorCustomizers.orderedStream()::iterator); + builder = builder.taskDecorator(this.taskDecorator.getIfUnique()); return builder; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java index 0112171fa7af..68c5e197072a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java @@ -39,6 +39,7 @@ * {@link TaskSchedulingAutoConfiguration} in a specific order. * * @author Moritz Halbritter + * @author Yanming Zhou */ class TaskSchedulingConfigurations { @@ -64,17 +65,38 @@ ThreadPoolTaskScheduler taskScheduler(ThreadPoolTaskSchedulerBuilder threadPoolT @Configuration(proxyBeanMethods = false) static class ThreadPoolTaskSchedulerBuilderConfiguration { + private final TaskSchedulingProperties properties; + + private final ObjectProvider threadPoolTaskSchedulerCustomizers; + + ThreadPoolTaskSchedulerBuilderConfiguration(TaskSchedulingProperties properties, + ObjectProvider threadPoolTaskSchedulerCustomizers) { + this.properties = properties; + this.threadPoolTaskSchedulerCustomizers = threadPoolTaskSchedulerCustomizers; + } + @Bean @ConditionalOnMissingBean - ThreadPoolTaskSchedulerBuilder threadPoolTaskSchedulerBuilder(TaskSchedulingProperties properties, - ObjectProvider threadPoolTaskSchedulerCustomizers) { - TaskSchedulingProperties.Shutdown shutdown = properties.getShutdown(); + @ConditionalOnThreading(Threading.PLATFORM) + ThreadPoolTaskSchedulerBuilder threadPoolTaskSchedulerBuilder() { + return builder(); + } + + @Bean(name = "threadPoolTaskSchedulerBuilder") + @ConditionalOnMissingBean + @ConditionalOnThreading(Threading.VIRTUAL) + ThreadPoolTaskSchedulerBuilder threadPoolTaskSchedulerBuilderVirtualThreads() { + return builder().virtualThreads(true); + } + + private ThreadPoolTaskSchedulerBuilder builder() { + TaskSchedulingProperties.Shutdown shutdown = this.properties.getShutdown(); ThreadPoolTaskSchedulerBuilder builder = new ThreadPoolTaskSchedulerBuilder(); - builder = builder.poolSize(properties.getPool().getSize()); + builder = builder.poolSize(this.properties.getPool().getSize()); builder = builder.awaitTermination(shutdown.isAwaitTermination()); builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod()); - builder = builder.threadNamePrefix(properties.getThreadNamePrefix()); - builder = builder.customizers(threadPoolTaskSchedulerCustomizers); + builder = builder.threadNamePrefix(this.properties.getThreadNamePrefix()); + builder = builder.customizers(this.threadPoolTaskSchedulerCustomizers); return builder; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfigurationTests.java index f8c6f2f337fb..1ad3fa5cbf73 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfigurationTests.java @@ -128,6 +128,23 @@ void threadPoolTaskExecutorBuilderShouldUseTaskDecorator() { }); } + @Test + void threadPoolTaskExecutorBuilderUsesPlatformThreadsByDefault() { + this.contextRunner.run((context) -> { + ThreadPoolTaskExecutorBuilder builder = context.getBean(ThreadPoolTaskExecutorBuilder.class); + assertThat(builder).hasFieldOrPropertyWithValue("virtualThreads", null); + }); + } + + @Test + @EnabledForJreRange(min = JRE.JAVA_21) + void threadPoolTaskExecutorBuilderUsesVirtualThreadsWhenEnabled() { + this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true").run((context) -> { + ThreadPoolTaskExecutorBuilder builder = context.getBean(ThreadPoolTaskExecutorBuilder.class); + assertThat(builder).hasFieldOrPropertyWithValue("virtualThreads", true); + }); + } + @Test void whenThreadPoolTaskExecutorIsAutoConfiguredThenItIsLazy() { this.contextRunner.run((context) -> { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfigurationTests.java index 74dc49d97403..48ecf2e4bf2f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfigurationTests.java @@ -56,6 +56,7 @@ * * @author Stephane Nicoll * @author Moritz Halbritter + * @author Yanming Zhou */ class TaskSchedulingAutoConfigurationTests { @@ -100,6 +101,28 @@ void enableSchedulingWithNoTaskExecutorAutoConfiguresOne() { }); } + @Test + @EnabledForJreRange(min = JRE.JAVA_21) + void threadPoolTaskSchedulerBuilderShouldUseVirtualThreadsIfEnabled() { + this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true") + .withUserConfiguration(SchedulingConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(ThreadPoolTaskSchedulerBuilder.class); + ThreadPoolTaskSchedulerBuilder builder = context.getBean(ThreadPoolTaskSchedulerBuilder.class); + assertThat(builder).hasFieldOrPropertyWithValue("virtualThreads", true); + }); + } + + @Test + @EnabledForJreRange(min = JRE.JAVA_21) + void threadPoolTaskSchedulerBuilderShouldUsePlatformThreadsByDefault() { + this.contextRunner.withUserConfiguration(SchedulingConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(ThreadPoolTaskSchedulerBuilder.class); + ThreadPoolTaskSchedulerBuilder builder = context.getBean(ThreadPoolTaskSchedulerBuilder.class); + assertThat(builder).hasFieldOrPropertyWithValue("virtualThreads", null); + }); + } + @Test void simpleAsyncTaskSchedulerBuilderShouldReadProperties() { this.contextRunner diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskExecutorBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskExecutorBuilder.java index fb90c8ad5898..33cd3674a603 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskExecutorBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskExecutorBuilder.java @@ -65,6 +65,8 @@ public class ThreadPoolTaskExecutorBuilder { private final TaskDecorator taskDecorator; + private final Boolean virtualThreads; + private final Set customizers; public ThreadPoolTaskExecutorBuilder() { @@ -78,13 +80,14 @@ public ThreadPoolTaskExecutorBuilder() { this.awaitTerminationPeriod = null; this.threadNamePrefix = null; this.taskDecorator = null; + this.virtualThreads = null; this.customizers = null; } private ThreadPoolTaskExecutorBuilder(Integer queueCapacity, Integer corePoolSize, Integer maxPoolSize, Boolean allowCoreThreadTimeOut, Duration keepAlive, Boolean acceptTasksAfterContextClose, Boolean awaitTermination, Duration awaitTerminationPeriod, String threadNamePrefix, - TaskDecorator taskDecorator, Set customizers) { + TaskDecorator taskDecorator, Boolean virtualThreads, Set customizers) { this.queueCapacity = queueCapacity; this.corePoolSize = corePoolSize; this.maxPoolSize = maxPoolSize; @@ -95,6 +98,7 @@ private ThreadPoolTaskExecutorBuilder(Integer queueCapacity, Integer corePoolSiz this.awaitTerminationPeriod = awaitTerminationPeriod; this.threadNamePrefix = threadNamePrefix; this.taskDecorator = taskDecorator; + this.virtualThreads = virtualThreads; this.customizers = customizers; } @@ -107,7 +111,8 @@ private ThreadPoolTaskExecutorBuilder(Integer queueCapacity, Integer corePoolSiz public ThreadPoolTaskExecutorBuilder queueCapacity(int queueCapacity) { return new ThreadPoolTaskExecutorBuilder(queueCapacity, this.corePoolSize, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination, - this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers); + this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads, + this.customizers); } /** @@ -122,7 +127,8 @@ public ThreadPoolTaskExecutorBuilder queueCapacity(int queueCapacity) { public ThreadPoolTaskExecutorBuilder corePoolSize(int corePoolSize) { return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, corePoolSize, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination, - this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers); + this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads, + this.customizers); } /** @@ -137,7 +143,8 @@ public ThreadPoolTaskExecutorBuilder corePoolSize(int corePoolSize) { public ThreadPoolTaskExecutorBuilder maxPoolSize(int maxPoolSize) { return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination, - this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers); + this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads, + this.customizers); } /** @@ -149,7 +156,8 @@ public ThreadPoolTaskExecutorBuilder maxPoolSize(int maxPoolSize) { public ThreadPoolTaskExecutorBuilder allowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) { return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination, - this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers); + this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads, + this.customizers); } /** @@ -160,7 +168,8 @@ public ThreadPoolTaskExecutorBuilder allowCoreThreadTimeOut(boolean allowCoreThr public ThreadPoolTaskExecutorBuilder keepAlive(Duration keepAlive) { return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, this.allowCoreThreadTimeOut, keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination, - this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers); + this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads, + this.customizers); } /** @@ -174,7 +183,8 @@ public ThreadPoolTaskExecutorBuilder keepAlive(Duration keepAlive) { public ThreadPoolTaskExecutorBuilder acceptTasksAfterContextClose(boolean acceptTasksAfterContextClose) { return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, acceptTasksAfterContextClose, this.awaitTermination, - this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers); + this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads, + this.customizers); } /** @@ -188,7 +198,8 @@ public ThreadPoolTaskExecutorBuilder acceptTasksAfterContextClose(boolean accept public ThreadPoolTaskExecutorBuilder awaitTermination(boolean awaitTermination) { return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, awaitTermination, - this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers); + this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads, + this.customizers); } /** @@ -203,7 +214,8 @@ public ThreadPoolTaskExecutorBuilder awaitTermination(boolean awaitTermination) public ThreadPoolTaskExecutorBuilder awaitTerminationPeriod(Duration awaitTerminationPeriod) { return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination, - awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers); + awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads, + this.customizers); } /** @@ -214,7 +226,8 @@ public ThreadPoolTaskExecutorBuilder awaitTerminationPeriod(Duration awaitTermin public ThreadPoolTaskExecutorBuilder threadNamePrefix(String threadNamePrefix) { return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination, - this.awaitTerminationPeriod, threadNamePrefix, this.taskDecorator, this.customizers); + this.awaitTerminationPeriod, threadNamePrefix, this.taskDecorator, this.virtualThreads, + this.customizers); } /** @@ -225,7 +238,20 @@ public ThreadPoolTaskExecutorBuilder threadNamePrefix(String threadNamePrefix) { public ThreadPoolTaskExecutorBuilder taskDecorator(TaskDecorator taskDecorator) { return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination, - this.awaitTerminationPeriod, this.threadNamePrefix, taskDecorator, this.customizers); + this.awaitTerminationPeriod, this.threadNamePrefix, taskDecorator, this.virtualThreads, + this.customizers); + } + + /** + * Specify whether to use virtual threads instead of platform threads. + * @param virtualThreads whether to use virtual threads instead of platform threads + * @return a new builder instance + */ + public ThreadPoolTaskExecutorBuilder virtualThreads(boolean virtualThreads) { + return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, + this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination, + this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, virtualThreads, + this.customizers); } /** @@ -255,7 +281,8 @@ public ThreadPoolTaskExecutorBuilder customizers(Iterable T configure(T taskExecutor) { map.from(this.awaitTerminationPeriod).as(Duration::toMillis).to(taskExecutor::setAwaitTerminationMillis); map.from(this.threadNamePrefix).whenHasText().to(taskExecutor::setThreadNamePrefix); map.from(this.taskDecorator).to(taskExecutor::setTaskDecorator); + map.from(this.virtualThreads).to(taskExecutor::setVirtualThreads); if (!CollectionUtils.isEmpty(this.customizers)) { this.customizers.forEach((customizer) -> customizer.customize(taskExecutor)); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilder.java index a36e48308ee4..981829431491 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ * bean and can be injected whenever a {@link ThreadPoolTaskScheduler} is needed. * * @author Stephane Nicoll + * @author Yanming Zhou * @since 3.2.0 */ public class ThreadPoolTaskSchedulerBuilder { @@ -48,6 +49,8 @@ public class ThreadPoolTaskSchedulerBuilder { private final String threadNamePrefix; + private final Boolean virtualThreads; + private final Set customizers; public ThreadPoolTaskSchedulerBuilder() { @@ -55,15 +58,18 @@ public ThreadPoolTaskSchedulerBuilder() { this.awaitTermination = null; this.awaitTerminationPeriod = null; this.threadNamePrefix = null; + this.virtualThreads = null; this.customizers = null; } public ThreadPoolTaskSchedulerBuilder(Integer poolSize, Boolean awaitTermination, Duration awaitTerminationPeriod, - String threadNamePrefix, Set taskSchedulerCustomizers) { + String threadNamePrefix, Boolean virtualThreads, + Set taskSchedulerCustomizers) { this.poolSize = poolSize; this.awaitTermination = awaitTermination; this.awaitTerminationPeriod = awaitTerminationPeriod; this.threadNamePrefix = threadNamePrefix; + this.virtualThreads = virtualThreads; this.customizers = taskSchedulerCustomizers; } @@ -74,7 +80,7 @@ public ThreadPoolTaskSchedulerBuilder(Integer poolSize, Boolean awaitTermination */ public ThreadPoolTaskSchedulerBuilder poolSize(int poolSize) { return new ThreadPoolTaskSchedulerBuilder(poolSize, this.awaitTermination, this.awaitTerminationPeriod, - this.threadNamePrefix, this.customizers); + this.threadNamePrefix, this.virtualThreads, this.customizers); } /** @@ -87,7 +93,7 @@ public ThreadPoolTaskSchedulerBuilder poolSize(int poolSize) { */ public ThreadPoolTaskSchedulerBuilder awaitTermination(boolean awaitTermination) { return new ThreadPoolTaskSchedulerBuilder(this.poolSize, awaitTermination, this.awaitTerminationPeriod, - this.threadNamePrefix, this.customizers); + this.threadNamePrefix, this.virtualThreads, this.customizers); } /** @@ -101,7 +107,7 @@ public ThreadPoolTaskSchedulerBuilder awaitTermination(boolean awaitTermination) */ public ThreadPoolTaskSchedulerBuilder awaitTerminationPeriod(Duration awaitTerminationPeriod) { return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, awaitTerminationPeriod, - this.threadNamePrefix, this.customizers); + this.threadNamePrefix, this.virtualThreads, this.customizers); } /** @@ -111,7 +117,17 @@ public ThreadPoolTaskSchedulerBuilder awaitTerminationPeriod(Duration awaitTermi */ public ThreadPoolTaskSchedulerBuilder threadNamePrefix(String threadNamePrefix) { return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod, - threadNamePrefix, this.customizers); + threadNamePrefix, this.virtualThreads, this.customizers); + } + + /** + * Specify whether to use virtual threads instead of platform threads. + * @param virtualThreads whether to use virtual threads instead of platform threads + * @return a new builder instance + */ + public ThreadPoolTaskSchedulerBuilder virtualThreads(boolean virtualThreads) { + return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod, + this.threadNamePrefix, virtualThreads, this.customizers); } /** @@ -143,7 +159,7 @@ public ThreadPoolTaskSchedulerBuilder customizers( Iterable customizers) { Assert.notNull(customizers, "Customizers must not be null"); return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod, - this.threadNamePrefix, append(null, customizers)); + this.threadNamePrefix, this.virtualThreads, append(null, customizers)); } /** @@ -173,7 +189,7 @@ public ThreadPoolTaskSchedulerBuilder additionalCustomizers( Iterable customizers) { Assert.notNull(customizers, "Customizers must not be null"); return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod, - this.threadNamePrefix, append(this.customizers, customizers)); + this.threadNamePrefix, this.virtualThreads, append(this.customizers, customizers)); } /** @@ -199,6 +215,7 @@ public T configure(T taskScheduler) { map.from(this.awaitTermination).to(taskScheduler::setWaitForTasksToCompleteOnShutdown); map.from(this.awaitTerminationPeriod).asInt(Duration::getSeconds).to(taskScheduler::setAwaitTerminationSeconds); map.from(this.threadNamePrefix).to(taskScheduler::setThreadNamePrefix); + map.from(this.virtualThreads).to(taskScheduler::setVirtualThreads); if (!CollectionUtils.isEmpty(this.customizers)) { this.customizers.forEach((customizer) -> customizer.customize(taskScheduler)); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskExecutorBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskExecutorBuilderTests.java index 8b8dc650e680..ceebe5426e0a 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskExecutorBuilderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskExecutorBuilderTests.java @@ -89,6 +89,12 @@ void taskDecoratorShouldApply() { assertThat(executor).extracting("taskDecorator").isSameAs(taskDecorator); } + @Test + void virtualThreadsShouldApply() { + ThreadPoolTaskExecutor executor = this.builder.virtualThreads(true).build(); + assertThat(executor).hasFieldOrPropertyWithValue("virtualThreads", true); + } + @Test void customizersWhenCustomizersAreNullShouldThrowException() { assertThatIllegalArgumentException() diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilderTests.java index 11b4f15f49af..4d4af667196e 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ * Tests for {@link ThreadPoolTaskSchedulerBuilder}. * * @author Stephane Nicoll + * @author Yanming Zhou */ class ThreadPoolTaskSchedulerBuilderTests { @@ -64,6 +65,12 @@ void threadNamePrefixShouldApply() { assertThat(scheduler.getThreadNamePrefix()).isEqualTo("test-"); } + @Test + void virtualThreadsShouldApply() { + ThreadPoolTaskScheduler scheduler = this.builder.virtualThreads(true).build(); + assertThat(scheduler).hasFieldOrPropertyWithValue("virtualThreads", true); + } + @Test void customizersWhenCustomizersAreNullShouldThrowException() { assertThatIllegalArgumentException()