Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

The easiest way to get started is to configure your IDE to use a recent Project Loom JDK 19 Early Access (EA) build and get familiar with using the java.lang.Thread API to create a virtual thread to execute some code. Virtual Threads threads are just threads that are scheduled by the Java virtual machine JDK rather than the operating system.  TheyVirtual threads are best suited to executing code that spends most of its time blocked, maybe waiting for a data to arrive on a network socket . Virtual threads are not suited to running code that is compute bound.or waiting for an element in queue for example.

Many applications won't use the Thread API directly but instead will use the In addition to to the Thread API, the java.util.concurrent.ExecutorService and Executors APIs are have . The Executors API has been updated to make it easy to work with virtual threads. Vrtual with new factory methods for ExecutorServices that start a new thread for each task. Virtual threads are cheap enough that a new virtual thread can be created for each task, no need for pooling of there should never be a need to pool virtual threads.

Thread API

The following uses a static factory method to create starts a virtual thread , invokes its start method to schedule it, and then invokes to print a message.  It invokes the join method to wait up to 5 seconds for the thread to terminate.

Code Block
languagejava
varThread thread = Thread.newThreadofVirtual(Thread.VIRTUAL, ).start(() -> System.out.println("helloHello"));
thread.start();
thread.join(Duration.ofSeconds(5));


The Thread.Builder API can also be used to create virtual threads. The first example creates a virtual thread but does not start it. The second example creates and starts the virtual threadfollowing is an example that start a virtual thread to put an element into a queue after sleeping. The main thread blocks on the queue, waiting for the element.

Code Block
languagejava
Thread thread1        var queue = Thread.builder().virtualnew SynchronousQueue<String>();

        Thread.ofVirtual().taskstart(() -> System.out.println("hello")).build();

Thread thread2 = Thread.builder().virtual().task(() -> System.out.println("hi")).start{
            try {
                Thread.sleep(Duration.ofSeconds(2));
                queue.put("done");
            } catch (InterruptedException e) { }
        });

        String msg = queue.take();


The Thread.Builder API can also be used to create a ThreadFactory. The ThreadFactory created by the following snippet will create virtual threads named "worker-0", "worker-1", "worker-2", ...

Code Block
languagejava
ThreadFactory factory = Thread.builderofVirtual().virtual().name("worker", 0).factory();


Executors/ExecutorService API

The following creates example uses the Executors API to create an ExecutorService that runs each task in its own virtual thread. The example runs two tasks and selects the result of the first task to complete. The other task is cancelled and the virtual thread running it is interruptedstarts a new virtual thread for each taskThe example uses the try-with-resources construct to ensure that the ExecutorService is shutdown and that all tasks (or virtual threads in this example) complete before continuing.the ExecutorService has terminated before continuing.

ExecutorService defines submit methods to execute tasks for execution. The submit methods don't block, instead they return a Future object that can be used to wait for the result or exception. The submit method that takes a collection of tasks returns a Stream is lazily populated with completed Future objects representing the results.

The example also uses the invokeAll and invokeAny combinator methods to execute several tasks and wait them to complete.                                          

Code Block
languagejava
        try (ExecutorService executor = Executors.newUnboundedVirtualThreadExecutornewVirtualThreadExecutor()) {

            // Submits a value-returning task and waits for the result
      Callable<String> task1      Future<String> future = executor.submit(() -> "foo");
    Callable<String> task2        String result = future.join();

  -> "bar";          // Submits two value-returning tasks to get a Stream that is lazily populated
            // with completed Future objects as the tasks complete
    String result        Stream<Future<String>> stream = executor.invokeAnysubmit(List.of(task1, task2));
}

The following creates an ExecutorService that runs each task in its own virtual thread. It adds a deadline to interrupt the threads that are still running when the deadline is reached.

Code Block
languagejava
Instant deadline = Instant.now().plusSeconds(30);
try (ExecutorService executor = Executors.newUnboundedExecutor(factory).withDeadline(deadline)) {
    :
}

Appendix: Differences between regular Threads and virtual Threads

The following is a list of the subtle differences between the two types of thread:

...

() -> "foo", () -> "bar"));
            stream.filter(Future::isCompletedNormally)
                    .map(Future::join)
                    .forEach(System.out::println);

            // Executes two value-returning tasks, waiting for both to complete
            List<Future<String>> results1 = executor.invokeAll(List.of(() -> "foo", () -> "bar"));

            // Executes two value-returning tasks, waiting for both to complete. If one of the
            // tasks completes with an exception, the other is cancelled.
            List<Future<String>> results2 = executor.invokeAll(List.of(() -> "foo", () -> "bar"), /*waitAll*/ false);

            // Executes two value-returning tasks, returning the result of the first to
            // complete, cancelling the other.
            String first = executor.invokeAny(List.of(() -> "foo", () -> "bar"));

        }