Java Virtual Threads


In Java, a virtual thread refers to a lightweight user-level thread of execution that is managed by the Java Virtual Machine (JVM) instead of relying on the underlying operating system threads. Virtual threads are also known as project Loom’s virtual threads or fibers.

Unlike traditional operating system threads, which are relatively heavyweight and can consume a significant amount of system resources, virtual threads are designed to be extremely lightweight. They can be created and scheduled by the JVM in large quantities, allowing for potentially massive concurrency without the same resource limitations as OS threads.

Virtual threads provide a more efficient concurrency model and can help alleviate issues associated with traditional thread-based programming, such as high memory usage and context switching overhead. They can be used to build highly scalable and responsive applications with improved resource utilization.

To work with virtual threads, you need a version of Java that supports project Loom. As of my knowledge cutoff in September 2021, project Loom is still in development and not yet included in the official Java releases. However, you can try out early access builds or preview versions to experiment with virtual threads.

Virtual threads, also known as fibers, are a new concurrency model introduced in Java to provide lightweight, user-level threads that can be managed by the Java Virtual Machine (JVM) instead of relying on the underlying operating system (OS) threads. Here’s an overview of how virtual threads work:

  1. Creation: Virtual threads are created using the Thread.startVirtualThread() method or by using Executors.newVirtualThreadExecutor() to obtain a virtual thread executor. Virtual threads can also be created indirectly by using higher-level abstractions like CompletableFuture or reactive streams.
  2. Scheduling: Virtual threads are scheduled and executed by the JVM’s thread scheduler. Unlike OS threads, virtual threads don’t require a dedicated OS thread for execution. Instead, a pool of OS threads, known as carrier threads, is used to execute virtual threads.
  3. Lightweight: Virtual threads are lightweight in terms of memory and resource usage compared to OS threads. They have a small stack size by default (e.g., 64KB), which can be dynamically resized based on the thread’s usage. This allows for efficient utilization of system resources, enabling the creation of large numbers of virtual threads.
  4. Cooperative Multitasking: Virtual threads follow a cooperative multitasking model, meaning they voluntarily yield control to other virtual threads. This differs from preemptive multitasking used by OS threads, where the operating system forcibly switches between threads. Cooperative multitasking eliminates the need for expensive context switches and reduces synchronization overhead.
  5. User-Level Synchronization: Virtual threads can use traditional synchronization mechanisms like locks, semaphores, and condition variables for thread synchronization. However, user-level synchronization can be more efficient due to the absence of expensive context switches.
  6. Event Loop Model: Virtual threads can be used in an event-driven programming model, similar to frameworks like Node.js. Applications can have a single event loop running on a virtual thread, handling multiple events asynchronously and efficiently.
  7. Scoped Execution: Virtual threads can be associated with a particular scope or context using Thread.withVirtualThread() method. This allows for context-specific behavior and control, such as thread-local variables or resource management within a specific scope.
  8. Compatibility: Virtual threads are designed to be compatible with existing Java libraries and frameworks. Existing code that uses traditional threads can be adapted to use virtual threads without significant changes.
  9. Debugging and Monitoring: Virtual threads can be debugged and monitored using existing debugging and profiling tools. The JVM provides information about virtual thread states, stack traces, and resource utilization, enabling effective troubleshooting and performance analysis.

Virtual Thread Creation Example:

  var t1= Thread.ofVirtual().name("t1").start(() -> {
      System.out.println("Virtual Thread Example");
  });

Example2:

Runnable r1= () -> {
    for(int i=0; i<10; i++) {
        System.out.println("Index: " + i);
    }
};

Thread virtualThread= Thread.ofVirtual().start(r1);

The above thread will start immediately and if you want to run thread later then:

Thread virtualThread= Thread.ofVirtual().unstarted(r1);

virtualThread.start(); // to start Later