Java concurrency in practice: virtual threads

Kostiantyn Ivanov
4 min readOct 21, 2023

--

What virtual thread is?

Virtual threads are lightweight threads that reduce the effort of writing, maintaining, and debugging high-throughput concurrent applications.

There are two kinds of threads, platform threads (we reviewed them in the previous article) and virtual threads.

A platform thread is implemented as a thin wrapper around an operating system (OS) thread. A platform thread runs Java code on its underlying OS thread, and the platform thread captures its OS thread for the platform thread’s entire lifetime. Consequently, the number of available platform threads is limited to the number of OS threads. Platform threads typically have a large thread stack and other resources that are maintained by the operating system. They are suitable for running all types of tasks but may be a limited resource.

Like a platform thread, a virtual thread is also an instance of java.lang.Thread. However, a virtual thread isn’t tied to a specific OS thread. A virtual thread still runs code on an OS thread. However, when code running in a virtual thread calls a blocking I/O operation, the Java runtime suspends the virtual thread until it can be resumed. The OS thread associated with the suspended virtual thread is now free to perform operations for other virtual threads. Virtual threads are implemented in a similar way to virtual memory. To simulate a lot of memory, an operating system maps a large virtual address space to a limited amount of RAM. Similarly, to simulate a lot of threads, the Java runtime maps a large number of virtual threads to a small number of OS threads. Unlike platform threads, virtual threads typically have a shallow call stack, performing as few as a single HTTP client call or a single JDBC query. Although virtual threads support thread-local variables and inheritable thread-local variables, you should carefully consider using them because a single JVM might support millions of virtual threads.

Virtual threads are suitable for running tasks that spend most of the time blocked, often waiting for I/O operations to complete. However, they aren’t intended for long-running CPU-intensive operations.

History

Project Loom (2017):

The Loom project was started to provide a functionality of virtual threads in java.

Java 19 (2022):

Virtual threads were proposed as a preview feature by JEP 425

Java 21 (2023):

Virtual threads are finalised and released.

Functionality:

One of the advantages of virtual threads is a compatibility with platform threads. They support the same syntax.

How to create a virtual thread:

Thread.startVirtualThread(new Runnable(){...});

Also we have a separate executor implementation for virtual threads (it’s not necessary to have it since anyway all the things will work on a ForkJoinPool. under the hood, but for applications that already work with executors it’s suitable to have it):

Executor executor = Executors.newVirtualThreadPerTaskExecutor();

Examples:

To demonstrate a power of virtual thread we will take one of the http servers from our previous article and modify it:

public class VirtualThreadHttpServer {
public static void main(String[] args) {
new VirtualThreadHttpServer().start();
}

private void start() {
int port = 8080;

try {
// Create a server socket on port 8080
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("Server listening on port " + port);
boolean first = true;

while (true) {
// Accept incoming client connections
Socket clientSocket = serverSocket.accept();
if (first) {
clientSocket.close();
first = false;
continue;
} else {
first = true;
}


System.out.println("Accepted connection from " + clientSocket.getInetAddress());

// Handle the client request
Thread.startVirtualThread(new SimpleWorker(clientSocket));

}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}

We will use the same testcase (300 parallel calls):

Just to remind, what the memory consumption was using a usual threads:

Now we call our version with virtual threads:

We handle the same amount of calls using much more less resources. More over, we didn’t create any thread pools or executors. All what we need — just to create a virtual thread with needed job assigned to it.
Under the hood we can see our old friend: ForkJoinPool.

Summary:

As we can see concurrency is still involving in java and latest releases bring us really interesting tools. Virtual threads have a great potential to make our java applications more resource-effective and parallel-friendly.

Links

Sources with examples: https://github.com/sIvanovKonstantyn/java-concurrency

Other articles of series:

--

--