web123456

Virtual Threads in SpringBoot - Birdshot for Birdshot

What are virtual threads

Virtual threading is a feature that was added at the beginning of Java 19 and is similar to theGolang(used form a nominal expression)Ctrip (Chinese company)Similarly, other languages have long been provided, and so practical and useful features, as a Java developer, has long been looking forward to.

Difference between virtual and normal threads

"Virtual" threads, literally, it is "fake", it is not directly scheduling the operating system threads, but by the JVM to provide a layer of thread interface abstraction, by the ordinary thread scheduling, that is, a common operating system thread can be scheduled thousands of virtual threads. Virtual threads consume much, much less than normal threads, and with enough memory, we can even create millions of virtual threads, which was not possible before (before Java19).

In fact, if you have used akka friends will find that, in fact, the two are very similar, except that the use of akka is the application to deal with, and the virtual thread is the JVM to deal with, the use of more concise and convenient.

SpringBoot using virtual threads

The following we will use virtual threads in SpringBoot, the default asynchronous thread pool and http processing thread pool to replace the virtual thread, and then compare the performance difference between virtual threads and ordinary threads, you will find that the difference is like a horse-drawn carriage for a high-speed train, not an era of things.

configure

First of all the version of Java we are using isjava-20.0.2-oracleThe SpringBoot version is3.1.2

To use virtual threads in SpringBoot is simple, add the following configuration:

  1. java copy code/**
  2. * Configuration is for later testing, -thread=true is to use virtual threads, false is to use the default normal threads
  3. */
  4. @Configuration
  5. @ConditionalOnProperty(prefix = "spring", name = "virtual-thread", havingValue = "true")
  6. public class ThreadConfig {
  7. @Bean
  8. public AsyncTaskExecutor applicationTaskExecutor() {
  9. return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
  10. }
  11. @Bean
  12. public TomcatProtocolHandlerCustomizer<?> protocolHandlerCustomizer() {
  13. return protocolHandler -> {
  14. protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
  15. };
  16. }
  17. }

@Async Performance Comparison

Let's write an asynchronous service that sleeps for 50ms inside, simulating MySQL orRedisand other IO operations:

  1. java copy code@Service
  2. public class AsyncService {
  3. /**
  4. *
  5. * @param countDownLatch for testing
  6. */
  7. @Async
  8. public void doSomething(CountDownLatch countDownLatch) throws InterruptedException {
  9. (50);
  10. ();
  11. }
  12. }

The final test class, quite simply, is to loop through the method 100,000 times and calculate the time consumed for all the methods to complete execution:

  1. java copy code @Test
  2. public void testAsync() throws InterruptedException {
  3. long start = ();
  4. int n = 100000;
  5. CountDownLatch countDownLatch = new CountDownLatch(n);
  6. for (int i = 0; i < n; i++) {
  7. (countDownLatch);
  8. }
  9. ();
  10. long end = ();
  11. ("Time consuming:" + (end - start) + "ms");
  12. }

Common threads take time: 678 seconds or so. That's over 10 minutes.

Virtual threads are time consuming: 3.9 seconds!!!

Friends, approaching200 timesThe performance gap!

HTTP Request Performance Comparison

Let's look at the comparison of http requests again, simply write a get request that does nothing in it and sleeps the same 50ms to simulate an IO operation:

  1. java copy code@RequestMapping("/get")
  2. public Object get() throws Exception {
  3. (50);
  4. return "ok";
  5. }

Then we use thejmeterrequest interface.500Concurrent threads that run10,000 times.And see how that works out:

General thread:

You can see that the minimum elapsed time50ms, there's nothing wrong with this, the interface sleeps for 50ms inside, but whether it's the median or the90/95/99 linesboth are larger than150msThis is because system threads are an expensive resource and SpringBoot'stomcatThe default maximum number of connections should be 200, after the threads in the connection pool are exhausted, these 200 threads are there waiting for 50ms to end, and the rest of the requests can only wait, unable to do anything else. Here's how the virtual thread behaves:

Virtual threads are time consuming:

It can be seen that even the maximum elapsed time stays below 100ms, i.e., the thread waiting time is significantly reduced and the virtual threads make better use of the system resources.

summarize

The performance comparison above shows that virtual threads have a clear advantage in terms of performance, but note that all of our tests above had the threads waiting for 50ms, what scenario is this simulating? That's right.IO-intensivescenario, i.e., the thread spends most of its time waiting for IO so that the virtual thread can take advantage of it, if theCPU-intensivescenarios, then it may not be very effective. However, most of our current applications are more IO-intensive applications, such as the typicalWEB applicationA lot of time on hold.Network IO(DB, caching, HTTP, etc.), the use of virtual threads is still very effective.

Finally: most companies are probably still using Java 8, but I'd say it's time to upgrade and get with the times, folks!