0Pricing
Java Academy · Урок

Создание виртуальных потоков

Thread.ofVirtual и исполнители

«Создание виртуальных потоков» — бесплатный урок Java Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.

Способы создания виртуальных потоков

В Java есть несколько способов создания виртуальных потоков. Два наиболее важных — это построитель Thread.ofVirtual() и фабрика Executors.newVirtualThreadPerTaskExecutor().

В этом уроке вы разберёте каждый из них.

Построитель: start

Thread.ofVirtual() возвращает построитель. Вызов .start(runnable) создаёт и немедленно запускает поток.

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread t = Thread.ofVirtual().start(() ->
            System.out.println("Started directly"));
        t.join();
    }
}

Построитель: unstarted

Используйте .unstarted(runnable), если хотите настроить поток и запустить его позже.

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread t = Thread.ofVirtual().unstarted(() ->
            System.out.println("Started later"));
        System.out.println("Before start, alive? " + t.isAlive());
        t.start();
        t.join();
    }
}

Многоразовая фабрика

Построитель может создавать ThreadFactory с помощью .factory(). Каждый вызов newThread создаёт новый виртуальный поток.

import java.util.concurrent.ThreadFactory;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ThreadFactory factory = Thread.ofVirtual().name("job-", 0).factory();
        Thread a = factory.newThread(() ->
            System.out.println(Thread.currentThread().getName()));
        Thread b = factory.newThread(() ->
            System.out.println(Thread.currentThread().getName()));
        a.start(); b.start();
        a.join(); b.join();
    }
}

Удобный метод

Thread.startVirtualThread(runnable) — это сокращённая запись для создания и запуска безымянного виртуального потока одним вызовом.

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread t = Thread.startVirtualThread(() ->
            System.out.println("One-liner virtual thread"));
        t.join();
    }
}

Подход с исполнителем

Для кода, организованного вокруг задач, предпочитайте Executors.newVirtualThreadPerTaskExecutor(). Он создаёт новый виртуальный поток для каждой переданной задачи.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args) {
        try (ExecutorService exec = Executors.newVirtualThreadPerTaskExecutor()) {
            exec.submit(() -> System.out.println("task one"));
            exec.submit(() -> System.out.println("task two"));
        }
        System.out.println("All tasks submitted and completed");
    }
}

Почему стоит использовать автоматическое закрытие ресурсов

Начиная с Java 19, ExecutorService реализует AutoCloseable. Закрытие исполнителя в блоке с автоматическим закрытием ресурсов ожидает завершения всех переданных задач.

Это обеспечивает корректное структурированное завершение работы без ручных вызовов shutdown и awaitTermination.

Передача множества задач

Исполнитель для каждой задачи легко справляется с огромным количеством задач. Каждая задача получает собственный виртуальный поток.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    public static void main(String[] args) {
        AtomicInteger sum = new AtomicInteger();
        try (ExecutorService exec = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 1; i <= 1000; i++) {
                final int n = i;
                exec.submit(() -> sum.addAndGet(n));
            }
        }
        System.out.println("Sum 1..1000 = " + sum.get());
    }
}

Сбор результатов с помощью отложенного результата

submit возвращает объект Future, поэтому вы можете собирать возвращаемые значения так же, как и при работе с любым исполнителем.

import java.util.concurrent.*;

public class Main {
    public static void main(String[] args) throws Exception {
        try (ExecutorService exec = Executors.newVirtualThreadPerTaskExecutor()) {
            Future<Integer> f = exec.submit(() -> 6 * 7);
            System.out.println("Result: " + f.get());
        }
    }
}

Построитель или исполнитель

Выбирайте подход в зависимости от цели:

  • Построитель / startVirtualThread для одноразовых потоков или настройки имён
  • newVirtualThreadPerTaskExecutor для передачи исполнителю наборов задач и сбора результатов

Оба подхода создают по одному виртуальному потоку на каждую единицу работы — именно то, что нужно.

Никогда не кэшируйте виртуальный исполнитель

Не используйте один исполнитель виртуальных потоков так, словно это фиксированный пул рабочих потоков. Сам исполнитель можно повторно использовать, но помните: каждая задача порождает совершенно новый поток.

Суть в том, что в пул объединяются не потоки, а лишь обеспечивается удобная передача задач исполнителю.

Быстрая проверка

Выберите лучший способ запустить большую серию задач, каждую в собственном виртуальном потоке.

Итоги

Теперь вы знаете, как создавать виртуальные потоки:

  • Thread.ofVirtual().start(...) и .unstarted(...)
  • однострочная запись Thread.startVirtualThread(...)
  • .factory() для многоразовой ThreadFactory
  • Executors.newVirtualThreadPerTaskExecutor() с автоматическим закрытием ресурсов

Далее вы узнаете, как выбирать между платформенными и виртуальными потоками.

Часто задаваемые вопросы

Урок «Создание виртуальных потоков» бесплатный?

Да — полный текст урока «Создание виртуальных потоков» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.

Чему я научусь в уроке «Создание виртуальных потоков»?

Thread.ofVirtual и исполнители Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Java Academy?

Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Создание виртуальных потоков»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Java Academy?

Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Что такое виртуальные потоки
  2. Создание виртуальных потоков
  3. Платформенные и виртуальные потоки
  4. Ошибки: закрепление и ThreadLocals
← Назад к Java Academy