Spring Virtual Threads
Overview
Virtual Threads
Virtual threads (Project Loom) is part of Java 21. Any blocking operation doesn't cause the thread to block. Thread Pool are replaced with a virtual thread executor.
Github: https://github.com/gitorko/project58
To enable virtual threads in spring boot application
1spring.threads.virtual.enabled=true
Code
1package com.demo.project58.controller;
2
3import java.util.List;
4import java.util.UUID;
5
6import com.demo.project58.pojo.Customer;
7import com.demo.project58.service.CustomerService;
8import lombok.RequiredArgsConstructor;
9import lombok.extern.slf4j.Slf4j;
10import org.springframework.data.domain.Page;
11import org.springframework.data.domain.PageRequest;
12import org.springframework.http.ResponseEntity;
13import org.springframework.transaction.annotation.Transactional;
14import org.springframework.web.bind.annotation.DeleteMapping;
15import org.springframework.web.bind.annotation.GetMapping;
16import org.springframework.web.bind.annotation.PathVariable;
17import org.springframework.web.bind.annotation.PostMapping;
18import org.springframework.web.bind.annotation.PutMapping;
19import org.springframework.web.bind.annotation.RequestBody;
20import org.springframework.web.bind.annotation.RequestMapping;
21import org.springframework.web.bind.annotation.RequestParam;
22import org.springframework.web.bind.annotation.RestController;
23import org.springframework.web.client.RestClient;
24
25@RestController
26@RequestMapping("/customer")
27@RequiredArgsConstructor
28@Slf4j
29public class HomeController {
30
31 final CustomerService customerService;
32 final RestClient restClient;
33
34 @PostMapping("/save")
35 @Transactional
36 public Customer saveCustomer(@RequestBody Customer customer) {
37 return customerService.save(customer);
38 }
39
40 @GetMapping("/all")
41 public List<Customer> findAll() {
42 return customerService.findAll();
43 }
44
45 @GetMapping("/{id}")
46 public Customer findById(@PathVariable UUID id) {
47 return customerService.findById(id);
48 }
49
50 @PutMapping(value = "/update")
51 public Customer update(@RequestBody Customer customer) {
52 return customerService.save(customer);
53 }
54
55 @DeleteMapping(value = "/{id}")
56 public void delete(@PathVariable UUID id) {
57 customerService.deleteById(id);
58 }
59
60 @GetMapping("/find")
61 public List<Customer> find(@RequestParam String name, @RequestParam Integer age) {
62 return customerService.findByNameAndAge(name, age);
63 }
64
65 @GetMapping("/page")
66 public Page<Customer> findPage(@RequestParam("page") int page, @RequestParam("size") int size) {
67 return customerService.findAll(PageRequest.of(page, size));
68 }
69
70 @GetMapping("/search")
71 public List<Customer> search(Customer customer) {
72 return customerService.search(customer);
73 }
74
75 @GetMapping("/block/{seconds}")
76 public String block(@PathVariable Integer seconds) {
77 ResponseEntity<Void> result = restClient.get()
78 .uri("/delay/" + seconds)
79 .retrieve()
80 .toBodilessEntity();
81
82 log.info("{} on {}", result.getStatusCode(), Thread.currentThread());
83 return Thread.currentThread().toString();
84 }
85}
1spring:
2 threads:
3 virtual:
4 enabled: true
5 task:
6 execution:
7 simple:
8 concurrency-limit: 10
9 scheduling:
10 simple:
11 concurrency-limit: 10
12 main:
13 banner-mode: "off"
14 datasource:
15 driver-class-name: org.postgresql.Driver
16 url: jdbc:postgresql://localhost:5432/test-db
17 username: test
18 password: test@123
19 jpa:
20 show-sql: false
21 liquibase:
22 enabled: true
23 change-log: db/changelog/db.changelog-main.yaml
24logging:
25 level:
26 org.springframework.data.jpa: DEBUG
Setup
1# Project 58
2
3Spring Virtual Threads
4
5[https://gitorko.github.io/spring-virtual-threads/](https://gitorko.github.io/spring-virtual-threads/)
6
7### Version
8
9Check version
10
11```bash
12$java --version
13openjdk 21
14```
15
16Check apache benchmark
17```bash
18$ ab -V
19This is ApacheBench, Version 2.3 <$Revision: 1903618 $>
20```
21
22### Postgres DB
23
24```
25docker run -p 5432:5432 --name pg-container -e POSTGRES_PASSWORD=password -d postgres:9.6.10
26docker ps
27docker exec -it pg-container psql -U postgres -W postgres
28CREATE USER test WITH PASSWORD 'test@123';
29CREATE DATABASE "test-db" WITH OWNER "test" ENCODING UTF8 TEMPLATE template0;
30grant all PRIVILEGES ON DATABASE "test-db" to test;
31
32docker stop pg-container
33docker start pg-container
34```
35
36### Dev
37
38To build the code.
39
40```bash
41./gradlew clean build
42```
43
44```bash
45ab -n 10 -c 2 http://localhost:8080/customer/block/3
46```
References
comments powered by Disqus