Skip to content

Commit d046f95

Browse files
committed
Add virtual thread option for ThreadPoolTaskExecutorBuilder/ThreadPoolTaskSchedulerBuilder
See spring-projects/spring-framework#33807
1 parent 3792772 commit d046f95

File tree

4 files changed

+80
-22
lines changed

4 files changed

+80
-22
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskExecutorBuilder.java

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ public class ThreadPoolTaskExecutorBuilder {
6565

6666
private final TaskDecorator taskDecorator;
6767

68+
private final Boolean virtualThreads;
69+
6870
private final Set<ThreadPoolTaskExecutorCustomizer> customizers;
6971

7072
public ThreadPoolTaskExecutorBuilder() {
@@ -78,13 +80,14 @@ public ThreadPoolTaskExecutorBuilder() {
7880
this.awaitTerminationPeriod = null;
7981
this.threadNamePrefix = null;
8082
this.taskDecorator = null;
83+
this.virtualThreads = null;
8184
this.customizers = null;
8285
}
8386

8487
private ThreadPoolTaskExecutorBuilder(Integer queueCapacity, Integer corePoolSize, Integer maxPoolSize,
8588
Boolean allowCoreThreadTimeOut, Duration keepAlive, Boolean acceptTasksAfterContextClose,
8689
Boolean awaitTermination, Duration awaitTerminationPeriod, String threadNamePrefix,
87-
TaskDecorator taskDecorator, Set<ThreadPoolTaskExecutorCustomizer> customizers) {
90+
TaskDecorator taskDecorator, Boolean virtualThreads, Set<ThreadPoolTaskExecutorCustomizer> customizers) {
8891
this.queueCapacity = queueCapacity;
8992
this.corePoolSize = corePoolSize;
9093
this.maxPoolSize = maxPoolSize;
@@ -95,6 +98,7 @@ private ThreadPoolTaskExecutorBuilder(Integer queueCapacity, Integer corePoolSiz
9598
this.awaitTerminationPeriod = awaitTerminationPeriod;
9699
this.threadNamePrefix = threadNamePrefix;
97100
this.taskDecorator = taskDecorator;
101+
this.virtualThreads = virtualThreads;
98102
this.customizers = customizers;
99103
}
100104

@@ -107,7 +111,8 @@ private ThreadPoolTaskExecutorBuilder(Integer queueCapacity, Integer corePoolSiz
107111
public ThreadPoolTaskExecutorBuilder queueCapacity(int queueCapacity) {
108112
return new ThreadPoolTaskExecutorBuilder(queueCapacity, this.corePoolSize, this.maxPoolSize,
109113
this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination,
110-
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers);
114+
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads,
115+
this.customizers);
111116
}
112117

113118
/**
@@ -122,7 +127,8 @@ public ThreadPoolTaskExecutorBuilder queueCapacity(int queueCapacity) {
122127
public ThreadPoolTaskExecutorBuilder corePoolSize(int corePoolSize) {
123128
return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, corePoolSize, this.maxPoolSize,
124129
this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination,
125-
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers);
130+
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads,
131+
this.customizers);
126132
}
127133

128134
/**
@@ -137,7 +143,8 @@ public ThreadPoolTaskExecutorBuilder corePoolSize(int corePoolSize) {
137143
public ThreadPoolTaskExecutorBuilder maxPoolSize(int maxPoolSize) {
138144
return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, maxPoolSize,
139145
this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination,
140-
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers);
146+
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads,
147+
this.customizers);
141148
}
142149

143150
/**
@@ -149,7 +156,8 @@ public ThreadPoolTaskExecutorBuilder maxPoolSize(int maxPoolSize) {
149156
public ThreadPoolTaskExecutorBuilder allowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {
150157
return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize,
151158
allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination,
152-
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers);
159+
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads,
160+
this.customizers);
153161
}
154162

155163
/**
@@ -160,7 +168,8 @@ public ThreadPoolTaskExecutorBuilder allowCoreThreadTimeOut(boolean allowCoreThr
160168
public ThreadPoolTaskExecutorBuilder keepAlive(Duration keepAlive) {
161169
return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize,
162170
this.allowCoreThreadTimeOut, keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination,
163-
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers);
171+
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads,
172+
this.customizers);
164173
}
165174

166175
/**
@@ -174,7 +183,8 @@ public ThreadPoolTaskExecutorBuilder keepAlive(Duration keepAlive) {
174183
public ThreadPoolTaskExecutorBuilder acceptTasksAfterContextClose(boolean acceptTasksAfterContextClose) {
175184
return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize,
176185
this.allowCoreThreadTimeOut, this.keepAlive, acceptTasksAfterContextClose, this.awaitTermination,
177-
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers);
186+
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads,
187+
this.customizers);
178188
}
179189

180190
/**
@@ -188,7 +198,8 @@ public ThreadPoolTaskExecutorBuilder acceptTasksAfterContextClose(boolean accept
188198
public ThreadPoolTaskExecutorBuilder awaitTermination(boolean awaitTermination) {
189199
return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize,
190200
this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, awaitTermination,
191-
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers);
201+
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads,
202+
this.customizers);
192203
}
193204

194205
/**
@@ -203,7 +214,8 @@ public ThreadPoolTaskExecutorBuilder awaitTermination(boolean awaitTermination)
203214
public ThreadPoolTaskExecutorBuilder awaitTerminationPeriod(Duration awaitTerminationPeriod) {
204215
return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize,
205216
this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination,
206-
awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.customizers);
217+
awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads,
218+
this.customizers);
207219
}
208220

209221
/**
@@ -214,7 +226,8 @@ public ThreadPoolTaskExecutorBuilder awaitTerminationPeriod(Duration awaitTermin
214226
public ThreadPoolTaskExecutorBuilder threadNamePrefix(String threadNamePrefix) {
215227
return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize,
216228
this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination,
217-
this.awaitTerminationPeriod, threadNamePrefix, this.taskDecorator, this.customizers);
229+
this.awaitTerminationPeriod, threadNamePrefix, this.taskDecorator, this.virtualThreads,
230+
this.customizers);
218231
}
219232

220233
/**
@@ -225,7 +238,20 @@ public ThreadPoolTaskExecutorBuilder threadNamePrefix(String threadNamePrefix) {
225238
public ThreadPoolTaskExecutorBuilder taskDecorator(TaskDecorator taskDecorator) {
226239
return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize,
227240
this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination,
228-
this.awaitTerminationPeriod, this.threadNamePrefix, taskDecorator, this.customizers);
241+
this.awaitTerminationPeriod, this.threadNamePrefix, taskDecorator, this.virtualThreads,
242+
this.customizers);
243+
}
244+
245+
/**
246+
* Specify whether to use virtual threads instead of platform threads.
247+
* @param virtualThreads whether to use virtual threads instead of platform threads
248+
* @return a new builder instance
249+
*/
250+
public ThreadPoolTaskExecutorBuilder virtualThreads(boolean virtualThreads) {
251+
return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize,
252+
this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination,
253+
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, virtualThreads,
254+
this.customizers);
229255
}
230256

231257
/**
@@ -255,7 +281,8 @@ public ThreadPoolTaskExecutorBuilder customizers(Iterable<? extends ThreadPoolTa
255281
Assert.notNull(customizers, "Customizers must not be null");
256282
return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize,
257283
this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination,
258-
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, append(null, customizers));
284+
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads,
285+
append(null, customizers));
259286
}
260287

261288
/**
@@ -284,7 +311,7 @@ public ThreadPoolTaskExecutorBuilder additionalCustomizers(
284311
Assert.notNull(customizers, "Customizers must not be null");
285312
return new ThreadPoolTaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize,
286313
this.allowCoreThreadTimeOut, this.keepAlive, this.acceptTasksAfterContextClose, this.awaitTermination,
287-
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator,
314+
this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, this.virtualThreads,
288315
append(this.customizers, customizers));
289316
}
290317

@@ -332,6 +359,7 @@ public <T extends ThreadPoolTaskExecutor> T configure(T taskExecutor) {
332359
map.from(this.awaitTerminationPeriod).as(Duration::toMillis).to(taskExecutor::setAwaitTerminationMillis);
333360
map.from(this.threadNamePrefix).whenHasText().to(taskExecutor::setThreadNamePrefix);
334361
map.from(this.taskDecorator).to(taskExecutor::setTaskDecorator);
362+
map.from(this.virtualThreads).to(taskExecutor::setVirtualThreads);
335363
if (!CollectionUtils.isEmpty(this.customizers)) {
336364
this.customizers.forEach((customizer) -> customizer.customize(taskExecutor));
337365
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilder.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,6 +36,7 @@
3636
* bean and can be injected whenever a {@link ThreadPoolTaskScheduler} is needed.
3737
*
3838
* @author Stephane Nicoll
39+
* @author Yanming Zhou
3940
* @since 3.2.0
4041
*/
4142
public class ThreadPoolTaskSchedulerBuilder {
@@ -48,22 +49,27 @@ public class ThreadPoolTaskSchedulerBuilder {
4849

4950
private final String threadNamePrefix;
5051

52+
private final Boolean virtualThreads;
53+
5154
private final Set<ThreadPoolTaskSchedulerCustomizer> customizers;
5255

5356
public ThreadPoolTaskSchedulerBuilder() {
5457
this.poolSize = null;
5558
this.awaitTermination = null;
5659
this.awaitTerminationPeriod = null;
5760
this.threadNamePrefix = null;
61+
this.virtualThreads = null;
5862
this.customizers = null;
5963
}
6064

6165
public ThreadPoolTaskSchedulerBuilder(Integer poolSize, Boolean awaitTermination, Duration awaitTerminationPeriod,
62-
String threadNamePrefix, Set<ThreadPoolTaskSchedulerCustomizer> taskSchedulerCustomizers) {
66+
String threadNamePrefix, Boolean virtualThreads,
67+
Set<ThreadPoolTaskSchedulerCustomizer> taskSchedulerCustomizers) {
6368
this.poolSize = poolSize;
6469
this.awaitTermination = awaitTermination;
6570
this.awaitTerminationPeriod = awaitTerminationPeriod;
6671
this.threadNamePrefix = threadNamePrefix;
72+
this.virtualThreads = virtualThreads;
6773
this.customizers = taskSchedulerCustomizers;
6874
}
6975

@@ -74,7 +80,7 @@ public ThreadPoolTaskSchedulerBuilder(Integer poolSize, Boolean awaitTermination
7480
*/
7581
public ThreadPoolTaskSchedulerBuilder poolSize(int poolSize) {
7682
return new ThreadPoolTaskSchedulerBuilder(poolSize, this.awaitTermination, this.awaitTerminationPeriod,
77-
this.threadNamePrefix, this.customizers);
83+
this.threadNamePrefix, this.virtualThreads, this.customizers);
7884
}
7985

8086
/**
@@ -87,7 +93,7 @@ public ThreadPoolTaskSchedulerBuilder poolSize(int poolSize) {
8793
*/
8894
public ThreadPoolTaskSchedulerBuilder awaitTermination(boolean awaitTermination) {
8995
return new ThreadPoolTaskSchedulerBuilder(this.poolSize, awaitTermination, this.awaitTerminationPeriod,
90-
this.threadNamePrefix, this.customizers);
96+
this.threadNamePrefix, this.virtualThreads, this.customizers);
9197
}
9298

9399
/**
@@ -101,7 +107,7 @@ public ThreadPoolTaskSchedulerBuilder awaitTermination(boolean awaitTermination)
101107
*/
102108
public ThreadPoolTaskSchedulerBuilder awaitTerminationPeriod(Duration awaitTerminationPeriod) {
103109
return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, awaitTerminationPeriod,
104-
this.threadNamePrefix, this.customizers);
110+
this.threadNamePrefix, this.virtualThreads, this.customizers);
105111
}
106112

107113
/**
@@ -111,7 +117,17 @@ public ThreadPoolTaskSchedulerBuilder awaitTerminationPeriod(Duration awaitTermi
111117
*/
112118
public ThreadPoolTaskSchedulerBuilder threadNamePrefix(String threadNamePrefix) {
113119
return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod,
114-
threadNamePrefix, this.customizers);
120+
threadNamePrefix, this.virtualThreads, this.customizers);
121+
}
122+
123+
/**
124+
* Specify whether to use virtual threads instead of platform threads.
125+
* @param virtualThreads whether to use virtual threads instead of platform threads
126+
* @return a new builder instance
127+
*/
128+
public ThreadPoolTaskSchedulerBuilder virtualThreads(boolean virtualThreads) {
129+
return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod,
130+
this.threadNamePrefix, virtualThreads, this.customizers);
115131
}
116132

117133
/**
@@ -143,7 +159,7 @@ public ThreadPoolTaskSchedulerBuilder customizers(
143159
Iterable<? extends ThreadPoolTaskSchedulerCustomizer> customizers) {
144160
Assert.notNull(customizers, "Customizers must not be null");
145161
return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod,
146-
this.threadNamePrefix, append(null, customizers));
162+
this.threadNamePrefix, this.virtualThreads, append(null, customizers));
147163
}
148164

149165
/**
@@ -173,7 +189,7 @@ public ThreadPoolTaskSchedulerBuilder additionalCustomizers(
173189
Iterable<? extends ThreadPoolTaskSchedulerCustomizer> customizers) {
174190
Assert.notNull(customizers, "Customizers must not be null");
175191
return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod,
176-
this.threadNamePrefix, append(this.customizers, customizers));
192+
this.threadNamePrefix, this.virtualThreads, append(this.customizers, customizers));
177193
}
178194

179195
/**
@@ -199,6 +215,7 @@ public <T extends ThreadPoolTaskScheduler> T configure(T taskScheduler) {
199215
map.from(this.awaitTermination).to(taskScheduler::setWaitForTasksToCompleteOnShutdown);
200216
map.from(this.awaitTerminationPeriod).asInt(Duration::getSeconds).to(taskScheduler::setAwaitTerminationSeconds);
201217
map.from(this.threadNamePrefix).to(taskScheduler::setThreadNamePrefix);
218+
map.from(this.virtualThreads).to(taskScheduler::setVirtualThreads);
202219
if (!CollectionUtils.isEmpty(this.customizers)) {
203220
this.customizers.forEach((customizer) -> customizer.customize(taskScheduler));
204221
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskExecutorBuilderTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ void taskDecoratorShouldApply() {
8989
assertThat(executor).extracting("taskDecorator").isSameAs(taskDecorator);
9090
}
9191

92+
@Test
93+
void virtualThreadsShouldApply() {
94+
ThreadPoolTaskExecutor executor = this.builder.virtualThreads(true).build();
95+
assertThat(executor).hasFieldOrPropertyWithValue("virtualThreads", true);
96+
}
97+
9298
@Test
9399
void customizersWhenCustomizersAreNullShouldThrowException() {
94100
assertThatIllegalArgumentException()

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilderTests.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@
3434
* Tests for {@link ThreadPoolTaskSchedulerBuilder}.
3535
*
3636
* @author Stephane Nicoll
37+
* @author Yanming Zhou
3738
*/
3839
class ThreadPoolTaskSchedulerBuilderTests {
3940

@@ -64,6 +65,12 @@ void threadNamePrefixShouldApply() {
6465
assertThat(scheduler.getThreadNamePrefix()).isEqualTo("test-");
6566
}
6667

68+
@Test
69+
void virtualThreadsShouldApply() {
70+
ThreadPoolTaskScheduler scheduler = this.builder.virtualThreads(true).build();
71+
assertThat(scheduler).hasFieldOrPropertyWithValue("virtualThreads", true);
72+
}
73+
6774
@Test
6875
void customizersWhenCustomizersAreNullShouldThrowException() {
6976
assertThatIllegalArgumentException()

0 commit comments

Comments
 (0)