Skip to content

Commit 17a09a4

Browse files
committed
[NO-TICKET] Use virtual threads (better solution without a pool)
1 parent 7858def commit 17a09a4

File tree

2 files changed

+67
-22
lines changed

2 files changed

+67
-22
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.getyourguide.openapi.validation.core.executor;
2+
3+
import java.util.concurrent.Executor;
4+
import java.util.concurrent.atomic.AtomicInteger;
5+
6+
public class VirtualThreadLimitedExecutor implements Executor {
7+
private static final int DEFAULT_MAX_CONCURRENT = 2;
8+
private final int maxConcurrent;
9+
private final AtomicInteger runningCount = new AtomicInteger(0);
10+
11+
public VirtualThreadLimitedExecutor() {
12+
this(DEFAULT_MAX_CONCURRENT);
13+
}
14+
15+
public VirtualThreadLimitedExecutor(int maxConcurrent) {
16+
checkVirtualThreadSupport();
17+
this.maxConcurrent = maxConcurrent;
18+
}
19+
20+
public static boolean isSupported() {
21+
try {
22+
checkVirtualThreadSupport();
23+
return true;
24+
} catch (UnsupportedOperationException | NoSuchMethodError e) {
25+
return false;
26+
}
27+
}
28+
29+
private static void checkVirtualThreadSupport() {
30+
// This will throw NoSuchMethodError on Java < 21
31+
//noinspection ResultOfMethodCallIgnored
32+
Thread.ofVirtual();
33+
}
34+
35+
@Override
36+
public void execute(Runnable command) {
37+
if (runningCount.get() >= maxConcurrent) {
38+
return;
39+
}
40+
41+
if (runningCount.incrementAndGet() > maxConcurrent) {
42+
runningCount.decrementAndGet();
43+
return;
44+
}
45+
46+
Thread.ofVirtual().start(() -> {
47+
try {
48+
command.run();
49+
} finally {
50+
runningCount.decrementAndGet();
51+
}
52+
});
53+
}
54+
}

spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/autoconfigure/LibraryAutoConfiguration.java

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.getyourguide.openapi.validation.core.OpenApiInteractionValidatorFactory;
2020
import com.getyourguide.openapi.validation.core.OpenApiRequestValidator;
2121
import com.getyourguide.openapi.validation.core.exclusions.InternalViolationExclusions;
22+
import com.getyourguide.openapi.validation.core.executor.VirtualThreadLimitedExecutor;
2223
import com.getyourguide.openapi.validation.core.log.DefaultOpenApiViolationHandler;
2324
import com.getyourguide.openapi.validation.core.log.ExclusionsOpenApiViolationHandler;
2425
import com.getyourguide.openapi.validation.core.log.ThrottlingOpenApiViolationHandler;
@@ -118,28 +119,18 @@ public OpenApiRequestValidator openApiRequestValidator(
118119
}
119120

120121
private Executor createThreadPoolExecutor() {
121-
try {
122-
// Try to use virtual threads if available (Java 21+)
123-
var virtualThreadFactory = Thread.ofVirtual().factory();
124-
return new ThreadPoolExecutor(
125-
2,
126-
2,
127-
1000L,
128-
TimeUnit.MILLISECONDS,
129-
new LinkedBlockingQueue<>(10),
130-
virtualThreadFactory,
131-
new ThreadPoolExecutor.DiscardPolicy()
132-
);
133-
} catch (UnsupportedOperationException | NoSuchMethodError e) {
134-
// Fallback to ThreadPoolExecutor with regular threads for older Java versions
135-
return new ThreadPoolExecutor(
136-
2,
137-
2,
138-
1000L,
139-
TimeUnit.MILLISECONDS,
140-
new LinkedBlockingQueue<>(10),
141-
new ThreadPoolExecutor.DiscardPolicy()
142-
);
122+
if (VirtualThreadLimitedExecutor.isSupported()) {
123+
return new VirtualThreadLimitedExecutor();
143124
}
125+
126+
// Fallback to ThreadPoolExecutor with regular threads for older Java versions
127+
return new ThreadPoolExecutor(
128+
2,
129+
2,
130+
1000L,
131+
TimeUnit.MILLISECONDS,
132+
new LinkedBlockingQueue<>(10),
133+
new ThreadPoolExecutor.DiscardPolicy()
134+
);
144135
}
145136
}

0 commit comments

Comments
 (0)