Skip to content

Virtual Threads

Sebastian Fischer edited this page Dec 11, 2023 · 19 revisions

Virtual Threads are lightweight threads supporting high-throughput concurrency. Most changes related to virtual threads in JDK 21 are invisible at an API level because virtual threads are instances of the familiar Thread class. To illustrate the changes, we will look at several examples of concurrent programs.

Blocking Operations

Virtual threads are beneficial with blocking operations like sleep. To compare different ways to execute tasks concurrently, we use them through a common interface ExecutorService which has implementations for different execution models such as pooling threads or creating new threads for each submitted task.

Sleep Sort is a tongue-in-cheek sorting algorithm that uses concurrent tasks to print each sorted item after waiting some time proportional to the item's size. The idea is to start all concurrent tasks simultaneously so they print their corresponding items in the correct order.

Here is a Java implementation of the Sleep Sort algorithm.

public record SleepSort(ExecutorService executor) {
    public static void main(String[] args) {
        new SleepSort().sortInputs();
    }
    private SleepSort() {
        this(Executors.newThreadPerTaskExecutor(Thread::new));
    }
    private void sortInputs() {
        try (BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in))) {
            stdin.lines().map(Integer::parseInt).forEach(num -> {
                executor.submit(() -> {
                    TimeUnit.SECONDS.sleep(num);
                    System.out.println(num);
                    return null;
                });
            });

            executor.shutdown();
            executor.awaitTermination(1, TimeUnit.MINUTES);
        } catch (IOException | InterruptedException e) {
            executor.shutdownNow();
        }
    }
}

We use Executors.newThreadPerTaskExecutor to create a new platform thread for every input number. In Java, each platform thread is mapped to an operating system thread. So, we create as many operating system threads as there are input numbers. For a small number of inputs, this approach works fine. The following shell command generates ten random numbers between 1 and 10 and then sorts them using our program.

me@java-dev:~/java21-demo$ shuf -r -i 1-10 -n 10 | java -cp target/classes sebfisch.SleepSort
1
2
3
3
5
5
5
8
9
10

When sorting a larger number of inputs, however, the time for creating as many operating system threads (for example 10000) seems to interfere with their sleeping time. As a result, numbers are not printed in sorted order.

me@java-dev:~/java21-demo$ shuf -r -i 1-10 -n 10000 | java -cp target/classes sebfisch.SleepSort
[skipped some output]
9
6
9
8
9
8
7
8
8
8
8
7
6
9
[skipped some output]

Clone this wiki locally