Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Как использовать виртуальные потоки

Короткий ответ

Виртуальные потоки (Java 21) очень лёгкие: их можно создавать тысячами. Проще всего — исполнитель, который запускает каждую задачу в новом виртуальном потоке:

void main() {
    var done = new AtomicInteger();
    long start = System.nanoTime();
 
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        for (int i = 0; i < 10_000; i++) {
            executor.submit(() -> {
                Thread.sleep(Duration.ofMillis(500));
                return done.incrementAndGet();
            });
        }
    }
 
    long ms = (System.nanoTime() - start) / 1_000_000;
    IO.println(done.get() + " tasks, less than 3 s: " + (ms < 3000));
}

Результат:

10000 tasks, less than 3 s: true

10 000 задач, каждая из которых ждёт полсекунды, завершаются примерно за полсекунды. С обычным пулом из 10 потоков это заняло бы больше восьми минут.

Один поток

Thread.ofVirtual().start() запускает один виртуальный поток, join() ждёт его завершения:

void main() throws InterruptedException {
    Thread thread = Thread.ofVirtual().start(() -> IO.println("Hello from a virtual thread"));
    thread.join();
 
    IO.println(thread.isVirtual());
}

Результат:

Hello from a virtual thread
true

Когда подходят

Виртуальные потоки хороши для задач, которые в основном ждут: запросы к сети, базе данных, файлам. Пока поток ждёт, JVM отдаёт процессор другим. Для тяжёлых вычислений они не ускоряют работу — число ядер процессора от них не растёт.

Частая ошибка

Складывать виртуальные потоки в пул фиксированного размера. Их не нужно экономить: создавайте новый поток на каждую задачу. Если нужно ограничить нагрузку, например число одновременных запросов к базе, используйте Semaphore.

Функции из этого ответа

  • Executors.newVirtualThreadPerTaskExecutor() — Создаёт сервис, который запускает каждую задачу в отдельном виртуальном потоке.
  • Thread.ofVirtual() — Создаёт виртуальный поток — лёгкий поток, которых можно запустить миллионы.
  • Thread.sleep() — Приостанавливает текущий поток на заданное время.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.