Java 21 Virtual Threads and Loom

Threads have been around since Java version 1 and they have proved useful when proving concurrency within systems. These threads are not going away it is just that a new type of thread is coming that is slightly more efficient to use in certain circumstances. It should be made clear that the new virtual thread paradigms are not a panacea or cure all for threading issues but work in a slightly different way that may help solve certain problems.

A normal thread in Java is known as a Platform thread. When you create one of these the JVM asks the OS for a thread and the OS obliges; however, the downside is that the OS also reserves some memory for each thread created. It also swaps threads in and out of the cache as context switching occurs which makes Platform threads quite cumbersome but the only alternative until the arrival of Virtual threads.

So what are virtual threads and how are they different to normal threads ? As I said the Platform threads come from the OS which has a limit on the number of threads and also provides some reserved memory per thread. Virtual threads come from and are managed by the JVM so there is no limit. It would be helpful here to talk about the motivation for virtual threads and why they were created. The issue that a lot of threads face is during their lifetime they can end up blocking or waiting for something to happen. When a thread is blocked it is still using system resource and consuming an OS thread resource which can get scarce. This means that a blocking operation can slow things down while you wait for the blocking to clear up. Think of a system that reaches out to other servers to get data, if this is in a thread it will block while we wait for the resource to reply and complete. This means that thread is out of action for the duration. Would it not be better if we could fire and forget that request and just re use that valuable thread resource for something else while we wait for the response as opposed to just blocking? We do not need the thread blocking while we wait, as we could reuse that thread and pick it up again once we have a reply. This is what virtual threads do.

This does mean that virtual threads will not speed up the compute intensive tasks but rather that the virtual thread will best shine when applied to blocking tasks that can cause the thread to wait for the blockage. In the recent versions of Java there has a been a lot of effort to change a lot of the underlying api's to become more non blocking and more Asynchronous because this allows things to speed up. This then implies that virtual threads are best designed for blocking tasks and you should create one thread per task that you need.

So what exactly are Virtual threads ? The JVM will create a virtual thread for you and manage it, and the JVM will assign the thread to a carrier thread which is itself a Platform thread. The key here is that the virtual thread can unmount from the carrier when it gets blocked and some other virtual thread can use the carrier so in this way we have a more efficient use of threads rather than waiting for a blocking scenario. There is however a caveat to this and that is a thread that gets pinned to the carrier and will not unmount. A thread gets pinned if:
  • It uses synchronized methods
  • it uses native methods
The jvm must keep the thread pinned under those circumstances to maintain consistency - however of you find that you need synchronization a clean way that allows for unmounting is to use ReentrantLock.

A rather excellent resource by Oracle on this subject which explains all this and more can be found here -> https://blogs.oracle.com/javamagazine/post/virtual-threads-futures

Now let us look at ways of creating virtual threads. All examples use the Java 21 idk

To create a simple Virtual thread we can do



Thread
thread= Thread.ofVirtual().start(() -> System.out.println("Simple thread") );
thread.join();





If you want it via Builder you can do



Thread.Builder builder = Thread.ofVirtual();
Thread
builderThread1 =builder.start(() -> System.out.println("Builder Thread"));
builderThread1.join();




It is best practice to create a thread per task in an Executor so that we can execute each task and it is best to use a Future to capture the result of that execution like this



try (ExecutorService myExecutor = Executors.newVirtualThreadPerTaskExecutor()) {
{
Future
future1 = myExecutor.submit(() -> System.out.println("Running thread via Executor 1"));
future1.get();
Future
future2 = myExecutor.submit(() -> System.out.println("Running thread via Executor 2"));
future2.get();
}

}
catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}




Now we can see threads being created with Futures that can hold their result, but would it not be better to have a way to just create a Virtual Thread Future that does all of this in one go and we just get back a Future that we can do what we wish with. This is accomplished by a simple Generic class that gives you a Future and runs a virtual thread with that Future under the covers like so. This is code from Oracle that was adapted and is in the link above.



public class VirtualFuture {
private final CompletableFuture theFuture;

public VirtualFuture(Supplierextends A> task) {
theFuture = new CompletableFuture<>();
Thread.startVirtualThread(() -> {
try {
theFuture.complete(task.get());
}
catch (Exception e) {
theFuture.completeExceptionally(e);
}
});
}

public A join() {
return theFuture.join();
}

public A get() {
try {
return theFuture.get();
}
catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
// export other methods as needed
}






Then you can easily use that to create a VirtualFuture and get the Future and use it to your hearts content. like this code snippet.






Worker
worker = new Worker();
VirtualFuture vFuture1 = new VirtualFuture<>(() -> worker.doWork("vFuture 1"));
vFuture1.join();
System.
out.println(vFuture1.get());

VirtualFuture vFuture2 = new VirtualFuture<>(() -> worker.doWork("vFuture 2"));
vFuture2.join();
System.
out.println(vFuture2.get());





I hope this has given you a flavour of what Virtual threads are and how they can be used in blocking tasks to improve their liveness.



People who enjoyed this article also enjoyed the following:


Java Interview Code Examples
Naive Bayes classification AI algorithm
K-Means Clustering AI algorithm
Equity Derivatives tutorial
Fixed Income tutorial

And the following Trails:

C++
Java
python
Scala
Investment Banking tutorials

HOME
homeicon




By clicking Dismiss you accept that you may get a cookie that is used to improve your user experience and for analytics.
All data is anonymised. Our privacy page is here =>
Privacy Policy
This message is required under GDPR (General Data Protection Rules ) and the ICO (Information Commissioners Office).