The easiest way to get started is to configure your IDE to use a recent Project Loom 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 are just threads that are scheduled by the Java virtual machine rather than the operating system. Virtual threads are best suited to executing code that spends most of its time blocked, waiting for a data to arrive on a network socket or waiting for an element in queue for example.
Many applications won't use the Thread API directly but instead will use the java.util.concurrent.ExecutorService and Executors APIs. The Executors API has been updated 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, there should never be a need to pool virtual threads.
The following uses a static factory method to start a virtual thread. It invokes the join method to wait for the thread to terminate.
The following is an example that creates a virtual thread that puts an element into a queue when it has completed a task. The main thread blocks on the queue, waiting for the element.
The Thread.Builder API can also be used to create virtual threads. The following has two code snippets. The first creates an un-started thread. The second creates and starts a thread with name "bob".
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", ...
The following example uses the Executors API to create an ExecutorService that starts a new virtual thread for each task. The example uses the try-with-resources construct to ensure that 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.