Управление потоками JVM против планирования ОС

Как я знаю, один из наиболее распространенных API параллелизма JVM: фьючерсы - по крайней мере, реализованные в scala - полагаются на пользовательский код для освобождения потока, когда он потенциально ожидает простоя. В scala это обычно называют "избеганием блокировок", и разработчик должен внедрять его везде, где это имеет смысл. Не совсем эффективно.

Есть ли что-то очень присущее JVM, которое препятствует переключению JVM контекста потока на новые задачи - когда поток простаивает - как это реализовано планировщиками процессов операционной системы?

1 ответ

Решение

Есть ли что-то очень присущее JVM, которое препятствует переключению JVM контекста потока на новые задачи - когда поток простаивает - как это реализовано планировщиками процессов операционной системы?

Главным образом необходимость того, чтобы такое переключение делалось совместно. Каждый отдельный метод блокировки должен быть упакован или повторно реализован таким образом, чтобы можно было возобновить задачу после ее завершения, в конце концов, нет никакого собственного потока, ожидающего завершения действия блокировки.

Хотя это может быть сделано в принципе для методов внутренней блокировки JVM, рассмотрим произвольный собственный код, выполняемый через JNI, JVM не будет знать, как стековать эти собственные потоки, в конце концов, они застряли в собственном коде.

Возможно, вы захотите взглянуть на квазар, так как я понимаю, что они реализовали такие оболочки или эквиваленты для некоторых внутренних методов JDK, таких как sleep, park/unparkКанальный ввод-вывод и множество других, которые позволяют своим волокнам (и, следовательно, фьючерсам, работающим на этих волокнах) точно выполнять переключение контекста пользовательского режима, пока они ожидают завершения.

Редактирование: одного JNI уже достаточно, чтобы ограничить переключение задач в пользовательском режиме, чтобы оно стало оппортунистической оптимизацией, которая, возможно, должна будет вернуться к раскрутке дополнительных собственных потоков, когда собственный код блокирует поток.

Но это не единственная проблема, например, для действительно асинхронных операций ввода-вывода в Linux требуется файловая система и поддержка ядра (см. Этот вопрос об AIO), что не все из них предоставляют. Там, где это не предусмотрено, его необходимо эмулировать, используя дополнительные блокирующие потоки ввода-вывода, таким образом заново вводя все издержки, которые мы хотели бы избежать в первую очередь. Можно просто заблокировать сам пул потоков и запустить дополнительные потоки, по крайней мере, мы избежим межпотокового взаимодействия таким образом.

Файлы, отображаемые в память, также могут блокировать поток и заставлять планировщик ОС приостанавливать поток из-за сбоев страниц, и я не знаю, как можно взаимодействовать с системой виртуальной памяти, чтобы избежать этого.

Не говоря уже о том, что все блокирующие вызовы на виртуальной машине должны быть повторно реализованы с использованием асинхронных эквивалентов, предоставляемых ОС. Пропустите хотя бы одну, и вы получите заблокированную ветку. Если у вас есть заблокированный поток, вашим пулам потоков понадобится функция автоматического увеличения, и мы вернемся к исходной точке.

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

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

Другие вопросы по тегам