0Pricing
Java Academy · Урок

Платформенные и виртуальные потоки

Когда использовать каждый вариант

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

Два вида потоков

Теперь в Java есть два вида потоков. Платформенные потоки — это классические оболочки над потоками OS. Виртуальные потоки лёгкие и управляются JVM.

Понимание того, когда выбирать каждый из них, — основа создания масштабируемых конкурентных приложений на Java.

Создание платформенного потока

Платформенный поток создаётся с помощью Thread.ofPlatform() — аналога Thread.ofVirtual().

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread p = Thread.ofPlatform().name("native-worker").start(() ->
            System.out.println("Platform? " + !Thread.currentThread().isVirtual()));
        p.join();
    }
}

Сравнение затрат

Платформенные потоки ресурсоёмки:

  • примерно 1 MB зарезервированного стека на каждый поток
  • Медленное создание
  • Количество ограничено несколькими тысячами

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

Задачи, связанные с вводом-выводом

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

Пока один виртуальный поток блокируется, его поток-носитель освобождается для выполнения других задач, поэтому нескольких потоков OS хватает для обслуживания тысяч запросов.

Вычислительные задачи

Для чистых вычислений, которые никогда не блокируются, виртуальные потоки не дают ускорения. Вы ограничены количеством ядер.

Здесь правильным инструментом будет фиксированный пул, размер которого соответствует числу процессоров.

import java.util.concurrent.*;

public class Main {
    public static void main(String[] args) throws Exception {
        int cores = Runtime.getRuntime().availableProcessors();
        try (ExecutorService cpu = Executors.newFixedThreadPool(cores)) {
            Future<Long> f = cpu.submit(() -> {
                long s = 0;
                for (long i = 0; i < 1_000_000L; i++) s += i;
                return s;
            });
            System.out.println("Cores: " + cores + ", sum: " + f.get());
        }
    }
}

Имитация блокировки

Этот фрагмент показывает, как множество виртуальных потоков одновременно спит, имитируя блокирующий ввод-вывод. Представьте, что 10 000 платформенных потоков делают то же самое.

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    public static void main(String[] args) {
        AtomicInteger done = new AtomicInteger();
        try (ExecutorService exec = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < 10_000; i++) {
                exec.submit(() -> {
                    try { Thread.sleep(50); } catch (InterruptedException e) {}
                    done.incrementAndGet();
                });
            }
        }
        System.out.println("Finished blocking tasks: " + done.get());
    }
}

Статус демона

Виртуальные потоки всегда являются потоками-демонами, и их нельзя сделать обычными потоками. Платформенные потоки по умолчанию наследуют статус демона создавшего их потока, но этот статус можно задать явно.

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread v = Thread.ofVirtual().unstarted(() -> {});
        Thread p = Thread.ofPlatform().unstarted(() -> {});
        System.out.println("virtual daemon: " + v.isDaemon());
        System.out.println("platform daemon: " + p.isDaemon());
    }
}

Приоритеты игнорируются

Виртуальные потоки всегда сообщают о приоритете NORM_PRIORITY, и его изменение ни на что не влияет. Планированием занимается JVM, а не планировщик OS.

public class Main {
    public static void main(String[] args) {
        Thread v = Thread.ofVirtual().unstarted(() -> {});
        v.setPriority(Thread.MAX_PRIORITY);
        System.out.println("Priority is still: " + v.getPriority());
    }
}

Правило выбора

Практическое правило:

  • Много задач, которые ожидают ввода-вывода? Используйте виртуальные потоки.
  • Тяжёлые вычисления, ограниченные возможностями CPU? Используйте фиксированный пул платформенных потоков.
  • Нужна настройка потоков на уровне OS, например приоритета или привязки? Используйте платформенные потоки.

Они сосуществуют

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

Оба вида используют один и тот же интерфейс Thread, поэтому их легко смешивать.

Совет по переносу

При переносе сервиса с интенсивным вводом-выводом проще всего заменить исполнителя: замените фиксированный или кэшируемый пул на newVirtualThreadPerTaskExecutor().

Будьте внимательны к закреплению потоков и злоупотреблению ThreadLocal — об этом рассказывается в следующем уроке.

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

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

Итоги

Вы сравнили две модели потоков:

  • Платформенные потоки: ресурсоёмкие, поддерживаемые OS, лучше всего подходят для вычислительных задач и настройки OS
  • Виртуальные потоки: дёшевы, управляются JVM, всегда являются потоками-демонами и лучше всего подходят для конкурентной работы с вводом-выводом
  • Они сосуществуют и используют один и тот же интерфейс

Далее: подводные камни закрепления потоков и ThreadLocals.

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

Урок «Платформенные и виртуальные потоки» бесплатный?

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

Чему я научусь в уроке «Платформенные и виртуальные потоки»?

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

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

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

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

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

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

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

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

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