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.extern.slf4j.Slf4j;
 9import org.springframework.beans.factory.annotation.Autowired;
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@Slf4j
28public class CustomerController {
29
30    @Autowired
31    private CustomerService customerService;
32
33    @Autowired
34    private RestClient restClient;
35
36    @GetMapping("/greet/{name}")
37    public String greet(@PathVariable String name) {
38        return customerService.greet(name);
39    }
40
41    @PostMapping("/save")
42    @Transactional
43    public Customer saveCustomer(@RequestBody Customer customer) {
44        return customerService.save(customer);
45    }
46
47    @GetMapping("/all")
48    public List<Customer> findAll() {
49        return customerService.findAll();
50    }
51
52    @GetMapping("/{id}")
53    public Customer findById(@PathVariable UUID id) {
54        return customerService.findById(id);
55    }
56
57    @PutMapping(value = "/update")
58    public Customer update(@RequestBody Customer customer) {
59        return customerService.save(customer);
60    }
61
62    @DeleteMapping(value = "/{id}")
63    public void delete(@PathVariable UUID id) {
64        customerService.deleteById(id);
65    }
66
67    @GetMapping("/find")
68    public List<Customer> find(@RequestParam String name, @RequestParam Integer age) {
69        return customerService.findByNameAndAge(name, age);
70    }
71
72    @GetMapping("/page")
73    public Page<Customer> findPage(@RequestParam("page") int page, @RequestParam("size") int size) {
74        return customerService.findAll(PageRequest.of(page, size));
75    }
76
77    @GetMapping("/search")
78    public List<Customer> search(Customer customer) {
79        return customerService.search(customer);
80    }
81
82    @GetMapping("/block/{seconds}")
83    public String block(@PathVariable Integer seconds) {
84        ResponseEntity<Void> result = restClient.get()
85                .uri("/delay/" + seconds)
86                .retrieve()
87                .toBodilessEntity();
88
89        log.info("{} on {}", result.getStatusCode(), Thread.currentThread());
90        return Thread.currentThread().toString();
91    }
92}
 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 & Unit Testing
 4
 5Checkstyle, SpotBugs, JaCoCo code coverage
 6
 7[https://gitorko.github.io/spring-virtual-threads/](https://gitorko.github.io/spring-virtual-threads/)
 8
 9### Version
10
11Check version
12
13```bash
14$java --version
15openjdk 21
16```
17
18Check apache benchmark
19```bash
20$ ab -V
21This is ApacheBench, Version 2.3 <$Revision: 1903618 $>
22```
23
24### Postgres DB
25
26```
27docker run -p 5432:5432 --name pg-container -e POSTGRES_PASSWORD=password -d postgres:9.6.10
28docker ps
29docker exec -it pg-container psql -U postgres -W postgres
30CREATE USER test WITH PASSWORD 'test@123';
31CREATE DATABASE "test-db" WITH OWNER "test" ENCODING UTF8 TEMPLATE template0;
32grant all PRIVILEGES ON DATABASE "test-db" to test;
33
34docker stop pg-container
35docker start pg-container
36```
37
38### Dev
39
40To build the code.
41
42```bash
43./gradlew clean build
44```
45
46```bash
47ab -n 10 -c 2 http://localhost:8080/customer/block/3
48```

References

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

comments powered by Disqus