Я очень увлечен Project Loom, но есть одна вещь, которую я не могу до конца понять.
Большинство серверов Java используют пулы потоков с определенным лимитом потоков (200, 300 ..), однако ОС не ограничивает вас в создании большего количества потоков, я читал, что с помощью специальных конфигураций для Linux вы можете достичь огромных чисел.
Потоки ОС более дороги и медленнее исполняются start/stop, вам приходится иметь дело с переключением контекста (увеличенным их количеством), и вы зависите от ОС, которая может отказаться предоставлять вам больше потоков.
Действительно, вы можете создавать миллионы виртуальных потоков
public static void main(String[] args) {
for (int i = 0; i < 1_000_000; i++) {
Thread.startVirtualThread(() -> {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
Приведенный выше код прерывается на отметке 25 КБ с исключением OOM, когда я использую потоки платформы.
Мой вопрос в том, что именно делает эти потоки такими легкими, что мешает нам создавать 1 миллион потоков платформы и работать с ними, только переключение контекста и использование оперативной памяти делают обычные потоки такими «тяжелыми»?
Один очень похожий вопрос
Вещи На данный момент я обнаружил:
Переключение контекста стоит дорого. Вообще говоря, даже в идеальном случае, когда ОС знает, как будут вести себя потоки, ей все равно придется предоставить каждому потоку равные шансы на выполнение, учитывая, что они имеют одинаковый приоритет. Если мы создадим 10 тысяч потоков ОС, то ей придется постоянно переключаться между ними, и одна эта задача в некоторых случаях может занимать до 80% процессорного времени, поэтому нам придется быть очень осторожными с цифрами. В Virtual Threads переключение контекста осуществляется JVM, что делает его практически бесплатным
Дешевый запуск/останов. Когда мы прерываем поток, мы, по сути, говорим задаче: «Уничтожьте поток ОС, в котором вы работаете». Однако, если, например, этот поток находится в пуле потоков, к моменту нашего запроса поток может быть освобожден текущей задачей, а затем передан другой, и другая задача может получить сигнал прерывания. Это делает процесс прерывания довольно сложным. Виртуальные потоки — это просто объекты, которые живут в куче, мы можем просто позволить сборщику мусора собирать их в фоновом режиме.
Жесткие верхние пределы (максимум десятки тысяч) потоков в зависимости от того, как ОС их обрабатывает. ОС не может быть точно настроена для конкретных приложений и языка программирования, поэтому ей приходится готовиться к худшему сценарию с точки зрения памяти. Ему необходимо выделить больше памяти, которая фактически будет использована для удовлетворения всех потребностей. При этом он должен гарантировать, что жизненно важные процессы ОС продолжают работать. С VT вы ограничены только дешевой памятью.
Поток, выполняющий транзакцию, ведет себя совсем иначе, чем поток, выполняющий обработку видео. Опять же, ОС должна подготовиться к наихудшему сценарию и приспособиться к обоим случаям наилучшим образом, что означает, что в большинстве случаев мы получаем неоптимальную производительность. Поскольку VT создаются и управляются самой Java, это обеспечивает полный контроль над ними и оптимизацию для конкретных задач, не привязанную к ОС.
Стек изменяемого размера. ОС предоставляет потокам большой стек, подходящий для всех вариантов использования. Виртуальные потоки имеют стек изменяемого размера, который находится в куче. Его размер динамически изменяется в соответствии с проблемой, что делает его меньше.
Меньший размер метаданных. Как упоминалось выше, потоки платформы используют 1 МБ, тогда как виртуальным потокам требуется 200–300 байт для хранения метаданных.
Обновление: 12 ноября 2024 г. (2 с половиной года спустя)
В отношении виртуальных машин произошло несколько важных изменений.
синхронизированный поток носителя делает миграцию устаревшей. код для ВТ невозможен (в основном из-за старых зависимостей). Готовится JEP-491, обещают решить эту проблему (скрестив пальцы)
локальные потоки слишком проблематичны, и они повсюду, а поскольку довольно много известных библиотек и фреймворков (MDC, Spring Boot и другие) используют миграцию локальных потоков потоков в виртуальные машины, все еще сложно. К счастью, JEP 446: Scoped Values находится в предварительной версии и может стать непредварительной функцией в Java 24 (скрестим пальцы x2).
структурированный параллелизм (JEP-543 Preview) не является проблемой, а является важным фактором при принятии решения о том, следует ли вам перейти на виртуальные машины. Это простой способ убедить вашу компанию перейти на виртуальные машины. Все еще находится в предварительной версии, снова ориентированной на Java 24.
В целом, это единственные три основных препятствия, которые остаются, прежде чем мы сможем полностью внедрить VT и попрощаться с библиотеками стилей async-await раз и навсегда (да, я говорю о вас, WebFlux )
Я очень увлечен Project Loom, но есть одна вещь, которую я не могу до конца понять. Большинство серверов Java используют пулы потоков с определенным лимитом потоков (200, 300 ..), однако ОС не ограничивает вас в создании большего количества потоков, я читал, что с помощью специальных конфигураций для Linux вы можете достичь огромных чисел. Потоки ОС более дороги и медленнее исполняются start/stop, вам приходится иметь дело с переключением контекста (увеличенным их количеством), и вы зависите от ОС, которая может отказаться предоставлять вам больше потоков. Действительно, вы можете создавать миллионы виртуальных потоков [code]public static void main(String[] args) { for (int i = 0; i < 1_000_000; i++) { Thread.startVirtualThread(() -> { try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } }); } } [/code] Приведенный выше код прерывается на отметке 25 КБ с исключением OOM, когда я использую потоки платформы. Мой вопрос в том, что именно делает эти потоки такими легкими, что мешает нам создавать 1 миллион потоков платформы и работать с ними, только переключение контекста и использование оперативной памяти делают обычные потоки такими «тяжелыми»? Один очень похожий вопрос Вещи На данный момент я обнаружил: [list] [*][b]Переключение контекста стоит дорого[/b]. Вообще говоря, даже в идеальном случае, когда ОС знает, как будут вести себя потоки, ей все равно придется предоставить каждому потоку равные шансы на выполнение, учитывая, что они имеют одинаковый приоритет. Если мы создадим 10 тысяч потоков ОС, то ей придется постоянно переключаться между ними, и одна эта задача в некоторых случаях может занимать до 80% процессорного времени, поэтому нам придется быть очень осторожными с цифрами. В Virtual Threads переключение контекста осуществляется JVM, что делает его практически бесплатным [*][b]Дешевый запуск/останов[/b]. Когда мы прерываем поток, мы, по сути, говорим задаче: «Уничтожьте поток ОС, в котором вы работаете». Однако, если, например, этот поток находится в пуле потоков, к моменту нашего запроса поток может быть освобожден текущей задачей, а затем передан другой, и другая задача может получить сигнал прерывания. Это делает процесс прерывания довольно сложным. Виртуальные потоки — это просто объекты, которые живут в куче, мы можем просто позволить сборщику мусора собирать их в фоновом режиме. [*][b]Жесткие верхние пределы[/b] (максимум десятки тысяч) потоков в зависимости от того, как ОС их обрабатывает. ОС не может быть точно настроена для конкретных приложений и языка программирования, поэтому ей приходится готовиться к худшему сценарию с точки зрения памяти. Ему необходимо выделить больше памяти, которая фактически будет использована для удовлетворения всех потребностей. При этом он должен гарантировать, что жизненно важные процессы ОС продолжают работать. С VT вы ограничены только дешевой памятью. [*][b]Поток, выполняющий транзакцию, ведет себя совсем иначе, чем поток, выполняющий обработку видео[/b]. Опять же, ОС должна подготовиться к наихудшему сценарию и приспособиться к обоим случаям наилучшим образом, что означает, что в большинстве случаев мы получаем неоптимальную производительность. Поскольку VT создаются и управляются самой Java, это обеспечивает полный контроль над ними и оптимизацию для конкретных задач, не привязанную к ОС. [*][b]Стек изменяемого размера[/b]. ОС предоставляет потокам большой стек, подходящий для всех вариантов использования. Виртуальные потоки имеют стек изменяемого размера, который находится в куче. Его размер динамически изменяется в соответствии с проблемой, что делает его меньше. [*][b]Меньший размер метаданных[/b]. Как упоминалось выше, потоки платформы используют 1 МБ, тогда как виртуальным потокам требуется 200–300 байт для хранения метаданных. [/list] Обновление: 12 ноября 2024 г. (2 с половиной года спустя) В отношении виртуальных машин произошло несколько важных изменений. [list] [*]синхронизированный поток носителя делает миграцию устаревшей. код для ВТ невозможен (в основном из-за старых зависимостей). Готовится JEP-491, обещают решить эту проблему (скрестив пальцы) [*]локальные потоки слишком проблематичны, и они повсюду, а поскольку довольно много известных библиотек и фреймворков (MDC, Spring Boot и другие) используют миграцию локальных потоков потоков в виртуальные машины, все еще сложно. К счастью, JEP 446: Scoped Values находится в предварительной версии и может стать непредварительной функцией в Java 24 (скрестим пальцы x2). [*]структурированный параллелизм (JEP-543 Preview) не является проблемой, а является важным фактором при принятии решения о том, следует ли вам перейти на виртуальные машины. Это простой способ убедить вашу компанию перейти на виртуальные машины. Все еще находится в предварительной версии, снова ориентированной на Java 24. [/list] В целом, это единственные три основных препятствия, которые остаются, прежде чем мы сможем полностью внедрить VT и попрощаться с библиотеками стилей async-await раз и навсегда (да, я говорю о вас, WebFlux 🦶🏼💥)