Open In App

Virtual Threads in Java

Last Updated : 15 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

In Java, Virtual threads are now supported by the Java Platform. Virtual threads are lightweight threads that greatly minimize the effort required to create, operate, and manage high volumes systems that are concurrent. As a result, they are more efficient and scalable than standard platform threads.

A thread is the smallest processing unit that can be scheduled. It operates concurrently with, and mostly independently of other units of this type. It’s an instance of java.lang.Thread.

There are two kinds of threads, platform threads and virtual threads:

  • Platform Threads
  • Virtual Threads

1. Platform Threads

Operating system (OS) threads are implemented with a platform thread acting as a thin wrapper around them. Java code is executed by a platform thread on its parent OS thread, and the platform thread captures its OS thread for the length of its lifetime.

As a result, there can only be an equal number of OS threads and platform threads.

The operating system usually maintains a large thread stack and additional resources for platform threads. They may be in short supply, but they are suitable for executing many kinds of work.

2. Virtual Threads

A virtual thread is an instance of java.lang.Thread, independent of any OS thread, is used to run programs. The Java runtime suspends the virtual thread until it resumes when the code calls a blocked I/O operation. Virtual threads have a limited call stack and can only execute one HTTP client call or JDBC query. They are suitable for delayed operations, but not for extended CPU-intensive tasks.

Syntax of Virtual Threads:

Thread virtualThread = Thread.ofVirtual().start(() -> {
// Code to be executed by the virtual thread
});

Why Use Virtual Thread?

In your high quantities concurrent applications, use virtual threads if you have many concurrent processes that take a long time to finish. Server applications often handle large numbers of client requests, which requires blocking I/O tasks such as resource access. This makes server applications high-throughput.

Virtual threads do not execute code more quickly than platform threads. Their goal is to provide scale (greater throughput) rather than speed (lower latency).

Advantages of Java Virtual Threads:

There are various advantages to using virtual threads:

  • Increases the availability of applications
  • Enhances application throughput.
  • Reduces the occurrence of ‘OutOfMemoryError: Unable to Create New Native Thread’.
  • Reduces the amount of memory used by the application
  • Enhances code quality
  • Platform Threads are completely compatible with them.

How To Create A Virtual Thread?

Platform and virtual thread creation is possible with the Thread and Thread.Builder APIs. The methods to build an ExecutorService that launches a new virtual thread for each operation are also defined in the java.util.concurrent.Executors class.

1. Using the Thread Class and the Thread.Builder Interface to Create a Virtual Thread

Create a version of Thread.Builder using the Thread.ofVirtual() function to build virtual threads.

The following example creates and opens a virtual thread that outputs a message. To wait for the virtual thread to finish its work, it calls the join method. You can see the written message before the main thread ends by doing this.

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

You can create threads with standard Thread properties, such as the thread name, using Thread.Builder. While Thread.Builder.OfVirtual creates virtual threads, Thread.Builder.OfPlatform creates platform threads.

The given code starts and opens a virtual thread called “MyThread,” prints a message while it’s operating, and then uses t.join() to wait for the thread to finish. You can follow these steps to a similar program:

Java




// Java program to demonstrate exchange
// Data between threads using scoped values
import java.util.*;
  
//Driver class
public class VirtualThreadExample {
      // main function
    public static void main(String[] args) {
        try {
            
              // Initialization of thread
            Thread.Builder builder = Thread.ofVirtual().name("GFG Thread");
  
            Runnable task = () -> {
                System.out.println("Running thread");
            };
  
            Thread t = builder.start(task);
  
            System.out.println("Thread t name: " + t.getName());
  
            // Add a delay to allow the virtual thread to run
              // Sleep for 1 second
            Thread.sleep(1000); 
  
            // Wait for the thread to complete
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


Output:

Running thread
Thread t name: GFG Thread

Explanation of the above program:

  • Using Thread.ofVirtual().name(“MyThread”), we establish a virtual thread.
  • Start the thread with a basic Runnable job that outputs “Running thread.”
  • We use t.getName() to print the thread’s name after it has been started.
  • To wait for the thread to finish, we use t.join().
  • Any InterruptedException that might be generated while the thread is running is identified by us.

This software will launch a virtual thread, carry all the task, print the thread name, and then wait for the thread to terminate.

2. Creating and executing a virtual thread with the Executors.newVirtualThreadPerTaskExecutor() Method

You can separate the creation and management of threads from other software components by using executors.

The Executors.newVirtualThreadPerTaskExecutor() function is used in the example below to build the ExecutorService. To complete the work, a new virtual thread is generated and launched when you use ExecutorService.submit(Runnable). A Future instance is returned by this method. It’s important to note that the Future.get() function waits for the thread to complete its task.

The code below code submits a basic task that prints a message and then uses future to wait for the task to finish using an ExecutorService with a virtual thread per task.fetch(). It prints “GeeksForGeeks” to signify that the task has been finished after it is finished.

This Java program represents the behavior in question:

Java




// Java Program for Creating and executing 
// Virtual thread with the 
// Executors.newVirtualThreadPerTaskExecutor() Method
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
  
public class VirtualThreadExecutorExample {
    public static void main(String[] args) {
        try (ExecutorService myExecutor = Executors.newVirtualThreadPerTaskExecutor()) {
            // Submit a task that prints a message
            Future<?> future = myExecutor.submit(() -> System.out.println("Running thread"));
  
            // Wait for the task to complete
            future.get();
  
            System.out.println("Program Completed !!");
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}


Output:

Running thread
Program Completed !!

Explanation of the above program:

  • We use virtual threads with executors to develop an executor service.virtualThreadPerTaskExecutor() is created.
  • We submit a basic task that uses myExecutor to print “Running thread”.submit(…) and take note of the Future object that the submission has returned.
  • We employ future.get() to pause when the job is finished. This will keep blocking till the job is completed.
  • “GeeksForGeeks” is printed to show that the task has been finished after it is finished.
  • To handle any exceptions that might arise, we catch ExecutionException and InterruptedException.

Multithreaded Client Server

The example that follows has two classes. A server application called EchoServer listens on a specific port and launches a fresh virtual thread with each new connection. A client application called EchoClient establishes a connection with the server and transmits commands submitted via the command line.

EchoClient connects to EchoServer via establishing a socket. It receives user input via the standard input stream, reads it, and writes the text to the socket to send it to EchoServer.The input is transmitted back to the EchoClient by the EchoServer over the socket. EchoClient receives data from the server and reads and shows it.

  • With virtual threads one for each client connection – EchoServer can serve numerous clients at once.

Below is the implementation of the above-mentioned topic:

Java




// Java Program to implement
// Multithreaded Client Server 
import java.io.*;
import java.net.*;
  
//Driver class
public class EchoServer {
    
      //Main method
    public static void main(String[] args) throws IOException {
        if (args.length != 1) {
            System.err.println("Usage: java EchoServer <port>");
            System.exit(1);
        }
  
        int portNumber = Integer.parseInt(args[0]);
        try (ServerSocket serverSocket = new ServerSocket(portNumber)) {
            System.out.println("EchoServer listening on port " + portNumber);
  
            while (true) {
                Socket clientSocket = serverSocket.accept();
                // Accept incoming connections
                // Start a service thread
                Thread.ofVirtual().start(() -> {
                    try (
                        PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
                        BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                    ) {
                        String inputLine;
                        while ((inputLine = in.readLine()) != null) {
                            System.out.println("Received: " + inputLine);
                            out.println("Echo: " + inputLine);
                            out.flush(); // Explicitly flush the output
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            clientSocket.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        } catch (IOException e) {
            System.out.println("Exception caught when trying to listen on port " + portNumber + " or listening for a connection");
            System.out.println(e.getMessage());
        }
    }
}


Output:

Usage: java EchoServer <port>

What is the performance impact of Virtual threads?

Platform threads are much heavier than Java virtual threads. More details about Java virtual threads’ impact on performance may be found here. However, to describe it briefly:

  • Looking for virtual threads will save memory if your program uses a lot of threads or a large stack size.
  • By using virtual threads instead of thread pools when your application often generates new threads, you can improve response time.

Frequently Asked Questions

1. Will virtual threads be in Java 21?

Virtual Threads is the most important update to JDK 21, the most recent LTS OpenJDK. Applications can be significantly more scalable thanks to virtual threads’ many-to-one mapping.

2. What are the advantages of virtual threads in Java?

Java Virtual Threads offer a strong and effective concurrency architecture for modern applications. Because virtual threads make concurrent programming easier to understand and enable more efficient use of resources, they have the potential to change how Java developers write concurrent programs.

3. What is the difference between virtual threads and OS threads?

An object in java.lang is a virtual thread.Thread that uses a core OS thread to run Java code, but does not hold onto the OS thread while the code is running.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads