Skip to content

Commit 8bc3133

Browse files
committed
first commit
1 parent 7469e02 commit 8bc3133

File tree

9 files changed

+203
-90
lines changed

9 files changed

+203
-90
lines changed

pom.xml

+15-21
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,21 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
43
<modelVersion>4.0.0</modelVersion>
54
<parent>
65
<groupId>org.springframework.boot</groupId>
76
<artifactId>spring-boot-starter-parent</artifactId>
87
<version>2.7.3</version>
9-
<relativePath/> <!-- lookup parent from repository -->
8+
<relativePath /> <!-- lookup parent from repository -->
109
</parent>
1110
<groupId>com.seven9nrh</groupId>
12-
<artifactId>sprbt-pr1</artifactId>
13-
<version>0.0.1-SNAPSHOT</version>
14-
<name>sprbt-pr1</name>
15-
<description>Demo project for Spring Boot</description>
11+
<artifactId>virtual-threads-app</artifactId>
12+
<version>1.0.0</version>
13+
<name>virtual-threads-app</name>
14+
<description>Sample application using virtual threads</description>
1615
<properties>
17-
<java.version>17</java.version>
16+
<java.version>19</java.version>
1817
</properties>
1918
<dependencies>
20-
<dependency>
21-
<groupId>org.springframework.boot</groupId>
22-
<artifactId>spring-boot-starter-security</artifactId>
23-
</dependency>
24-
<dependency>
25-
<groupId>org.springframework.boot</groupId>
26-
<artifactId>spring-boot-starter-web</artifactId>
27-
</dependency>
28-
2919
<dependency>
3020
<groupId>org.springframework.boot</groupId>
3121
<artifactId>spring-boot-devtools</artifactId>
@@ -38,9 +28,13 @@
3828
<scope>test</scope>
3929
</dependency>
4030
<dependency>
41-
<groupId>org.springframework.security</groupId>
42-
<artifactId>spring-security-test</artifactId>
43-
<scope>test</scope>
31+
<groupId>org.springframework.boot</groupId>
32+
<artifactId>spring-boot-starter-webflux</artifactId>
33+
</dependency>
34+
35+
<dependency>
36+
<groupId>org.springframework.boot</groupId>
37+
<artifactId>spring-boot-starter-thymeleaf</artifactId>
4438
</dependency>
4539
</dependencies>
4640

@@ -53,4 +47,4 @@
5347
</plugins>
5448
</build>
5549

56-
</project>
50+
</project>

src/main/java/com/seven9nrh/sprbtpr1/SprbtPr1Application.java

-13
This file was deleted.

src/main/java/com/seven9nrh/sprbtpr1/application/config/WebConfig.java

-25
This file was deleted.

src/main/java/com/seven9nrh/sprbtpr1/application/config/WebSecurityConfig.java

-18
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.seven9nrh.virtualthreads;
2+
3+
import java.util.concurrent.Executor;
4+
import org.springframework.boot.SpringApplication;
5+
import org.springframework.boot.autoconfigure.SpringBootApplication;
6+
import org.springframework.context.annotation.Bean;
7+
import org.springframework.scheduling.annotation.EnableAsync;
8+
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
9+
10+
@SpringBootApplication
11+
@EnableAsync
12+
public class VirtualThreadsApplication {
13+
14+
public static void main(String[] args) {
15+
SpringApplication.run(VirtualThreadsApplication.class, args);
16+
}
17+
18+
@Bean("asyncThread")
19+
public Executor taskExecutor() {
20+
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
21+
executor.setCorePoolSize(1000);
22+
executor.setMaxPoolSize(Integer.MAX_VALUE);
23+
executor.setQueueCapacity(9999);
24+
executor.setThreadFactory(Thread.ofVirtual().factory()); // <---- Enable Virtual Threads
25+
return executor;
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.seven9nrh.virtualthreads.application;
2+
3+
import com.seven9nrh.virtualthreads.service.ThreadsService;
4+
import com.seven9nrh.virtualthreads.service.ThreadsService.ThreadResult;
5+
import org.springframework.stereotype.Controller;
6+
import org.springframework.ui.Model;
7+
import org.springframework.web.bind.annotation.RequestMapping;
8+
import org.springframework.web.bind.annotation.RequestParam;
9+
import org.thymeleaf.spring5.context.webflux.IReactiveDataDriverContextVariable;
10+
import org.thymeleaf.spring5.context.webflux.ReactiveDataDriverContextVariable;
11+
import reactor.core.publisher.Flux;
12+
13+
@Controller
14+
public class ThreadsController {
15+
16+
ThreadsService threadService;
17+
18+
public ThreadsController(ThreadsService threadService) {
19+
this.threadService = threadService;
20+
}
21+
22+
@RequestMapping
23+
public String init(Model model) {
24+
return "threads";
25+
}
26+
27+
@RequestMapping("/threads")
28+
public String start(
29+
Model model,
30+
@RequestParam(name = "num", required = true) int num
31+
) {
32+
Flux<ThreadResult> flux = threadService.execute(num);
33+
IReactiveDataDriverContextVariable reactiveDataDriverContextVariable = new ReactiveDataDriverContextVariable(
34+
flux,
35+
1
36+
);
37+
model.addAttribute("threadResults", reactiveDataDriverContextVariable);
38+
return "threads";
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package com.seven9nrh.virtualthreads.service;
2+
3+
import java.time.Duration;
4+
import java.time.LocalDateTime;
5+
import java.time.temporal.ChronoUnit;
6+
import java.util.concurrent.CompletableFuture;
7+
import org.springframework.context.ApplicationContext;
8+
import org.springframework.scheduling.annotation.Async;
9+
import org.springframework.stereotype.Service;
10+
import reactor.core.publisher.Flux;
11+
12+
@Service
13+
public class ThreadsService {
14+
15+
private ApplicationContext applicationContext;
16+
17+
public ThreadsService(ApplicationContext applicationContext) {
18+
this.applicationContext = applicationContext;
19+
}
20+
21+
public Flux<ThreadResult> execute(int num) {
22+
ThreadsService service = applicationContext.getBean(ThreadsService.class);
23+
// stop if num is less than 1
24+
if (num < 1) {
25+
return Flux.empty();
26+
}
27+
CompletableFuture<ThreadResult>[] futures = new CompletableFuture[num];
28+
return Flux.<ThreadResult>create(
29+
sink -> {
30+
for (int i = 0; i < num; i++) {
31+
CompletableFuture<ThreadResult> future = service.executeAsync();
32+
futures[i] = future;
33+
future.thenAccept(action -> sink.next(action));
34+
}
35+
CompletableFuture.allOf(futures).thenRunAsync(() -> sink.complete());
36+
}
37+
);
38+
}
39+
40+
@Async("asyncThread")
41+
public CompletableFuture<ThreadResult> executeAsync() {
42+
LocalDateTime start = LocalDateTime.now();
43+
String threadName = String.valueOf(Thread.currentThread().threadId());
44+
try {
45+
Thread.sleep((long) (Math.floor(Math.random() * 11) * 1000));
46+
} catch (InterruptedException e) {
47+
e.printStackTrace();
48+
}
49+
LocalDateTime end = LocalDateTime.now();
50+
Duration duration = Duration.between(start, end);
51+
var threadResult = new ThreadResult(
52+
start.truncatedTo(ChronoUnit.SECONDS),
53+
end.truncatedTo(ChronoUnit.SECONDS),
54+
duration.getSeconds(),
55+
threadName
56+
);
57+
return CompletableFuture.completedFuture(threadResult);
58+
}
59+
60+
public static class ThreadResult {
61+
62+
private LocalDateTime start;
63+
private LocalDateTime end;
64+
private long duration;
65+
private String threadName;
66+
67+
public ThreadResult(
68+
LocalDateTime start,
69+
LocalDateTime end,
70+
long duration,
71+
String threadName
72+
) {
73+
this.start = start;
74+
this.end = end;
75+
this.duration = duration;
76+
this.threadName = threadName;
77+
}
78+
79+
public LocalDateTime getStart() {
80+
return start;
81+
}
82+
83+
public LocalDateTime getEnd() {
84+
return end;
85+
}
86+
87+
public long getDuration() {
88+
return duration;
89+
}
90+
91+
public String getThreadName() {
92+
return threadName;
93+
}
94+
}
95+
}
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<!DOCTYPE html>
2+
<html xmlns:th="http://www.thymeleaf.org">
3+
<head>
4+
<title>Thread Result</title>
5+
<meta charset="UTF-8" />
6+
<title>Thread result</title>
7+
<link
8+
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
9+
rel="stylesheet"
10+
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
11+
crossorigin="anonymous"
12+
/>
13+
</head>
14+
15+
<body>
16+
<form th:action="@{/threads}">
17+
<div class="d-flex flex-row">
18+
<input type="number" th:name="num" class="form-control"/>
19+
<button type="submit" class="btn btn-primary">START</button>
20+
</div>
21+
</form>
22+
<div data-th-each="threadResult:${threadResults}">
23+
[[${threadResult.threadName}]] : [[${threadResult.start}]] - [[${threadResult.end}]] | [[${threadResult.duration}]]
24+
</div>
25+
<body>
26+
</html>

src/test/java/com/seven9nrh/sprbtpr1/SprbtPr1ApplicationTests.java

-13
This file was deleted.

0 commit comments

Comments
 (0)