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

https://spring.io/blog/2022/10/11/embracing-virtual-threads

comments powered by Disqus