Recently I explored virtual threads running Helidon 4.0.0-M1 on Java 20. In this article I will share some things I learned along the way and an example project. I plan to go more in depth in future posts.
Virtual Threads
"Virtual threads are lightweight threads that dramatically reduce the effort of writing, maintaining, and observing high-throughput concurrent applications."
...virtual threads can significantly improve application throughput when the number of concurrent tasks is high (more than a few thousand), and the workload is not CPU-bound, since having many more threads than processor cores cannot improve throughput in that case.
Virtual threads:
- increase throughput, not speed
- are provided by the JDK (not the OS)
- share OS threads - M:N
- vs. a platform thread that "captures" an OS thread - 1:1
- vs. outdated "green threads" - M:1
- release ("unmount") the OS carrier thread when blocked*
- except when:
- executing code in a synchronized block or method
- executing native method or foreign function
- a JDK Flight Recorder (JFR) event is emitted when thread blocks when pinned
- can trigger a stack trace when thread blocks while pinned via: jdk.tracePinnedThreads=full|short
- are an instance of java.lang.Thread
- support thread local variables
- support thread interruption
- are usually short lived
- are debuggable
- but dump differently (say wat?) bc scale
- possibly millions of virtual threads
- are scheduled differently
- scheduled by JVM not the OS
- the JVM uses a work-stealing ForkJoinPool of platform threads (not the common pool)
- the number of platform threads in this pool defaults to the number of processors
- can be tuned with system properties: jdk.virtualThreadScheduler.parallelism and jdk.virtualThreadScheduler.maxPoolSize
- have no visibility to their "carrier" platform thread when executing
- stacks are stored in the heap as "stack chunk" objects
- stack chunks are not GC roots
- stack chunks can not be humongous
- should never be pooled
Virtual threads were first introduced with Java 17. The current version (as of this writing) of Java is version 20 and virtual threads are still a "preview" feature. However, Java 21, an LTS (long term support), is just around the corner with a GA release targeted for 9/19/2023.
Helidon
Helidon is a cloud-native, open‑source set of Java libraries for writing microservices that run on a fast web core powered by Netty.* (well not exactly Netty)
Helidon Níma is the first Java microservices framework based on virtual threads.
When I began looking into frameworks that support virtual threads, I first checked whether Undertow supported virtual threads. In a past life, I built a private framework based on Undertow and Guice and I still feel pretty good about this approach. But, Undertow… nope.
I then looked into frameworks like Micronaut and Quarkus. Both have a lot to offer off the click. However, I really like control and I wish that the config and the DI capabilities could be dynamically configured per preference. Both of these frameworks adopted a DI interface (eg. javax.inject or jakarta.inject) yet prevent or limit configurability.
IMO, Helidon is a lightweight, embeddable framework and, hey!, it supports virtual threads (v4.0.0-M1).
How To
Code speaks louder than words.
Random Jots
Local Tools
> gradle -v
Gradle 8.2.1
Build time: 2023-07-10 12:12:35 UTC
Revision: a38ec64d3c4612da9083cc506a1ccb212afeecaa
Kotlin: 1.8.20
Groovy: 3.0.17
Ant: Apache Ant(TM) version 1.10.13 compiled on January 4 2023
JVM: 20.0.1 (Eclipse Adoptium 20.0.1+9)
OS: Mac OS X 13.4 x86_64
How to Local
> git clone git@github.com:beyondcivil/pub.git
> cd pub/helidon4M1jdk20
> gradle clean build
> java --enable-preview -jar app/build/libs/app-all.jar
Aug 16, 2023 3:31:31 PM io.helidon.common.features.HelidonFeatures features
INFO: Helidon NIMA 4.0.0-M1 features: [WebServer]
Aug 16, 2023 3:31:31 PM io.helidon.nima.webserver.ServerListener start
INFO: [0x51fadaff] http://0.0.0.0:9710 bound for socket '@default'
Aug 16, 2023 3:31:31 PM io.helidon.nima.webserver.LoomServer startIt
INFO: Started all channels in 46 milliseconds. 1093 milliseconds since JVM startup. Java 20.0.1+9
> curl 'http://localhost:9710/greeting'
Print value of Thread.currentThread()
VirtualThread[#31,[0x38234a38 0x33cc5fd8] Nima socket]/runnable@ForkJoinPool-1-worker-1
Helidon 4M1, Java 20, and Virtual Threads Pt. 1 - Getting Started
Part 1 - Random thoughts along my adventure using Java 20 virtual threads on Helidon 4.0.0-M1.