Рубрики
Без рубрики

Ткацкий станок проекта OpenJDK

Узнайте о попытке Project Loom внедрить облегченную конструкцию параллелизма в Java

Автор оригинала: baeldung.

1. Обзор

В этой статье мы кратко рассмотрим Project Loom . По сути, основной целью Project Loom является поддержка высокопроизводительной, облегченной модели параллелизма в Java.

2. Проектный ткацкий станок

Project Loom – это попытка сообщества OpenJDK внедрить в Java облегченную конструкцию параллелизма. Прототипы для Loom до сих пор вносили изменения в JVM, а также в библиотеку Java.

Хотя запланированного выпуска Loom еще нет, мы можем получить доступ к последним прототипам на Project Looks wiki .

Прежде чем мы обсудим различные концепции Loom, давайте обсудим текущую модель параллелизма в Java.

3. Модель параллелизма Java

В настоящее время Thread представляет собой основную абстракцию параллелизма в Java. Эта абстракция, наряду с другими параллельными API , упрощает написание параллельных приложений.

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

  1. Потоки не могут соответствовать масштабу единицы параллелизма домена. Например, приложения обычно допускают до миллионов транзакций, пользователей или сеансов. Однако количество потоков, поддерживаемых ядром, намного меньше. Таким образом, a T thread для каждого пользователя, транзакции или сеанса часто неосуществимо.
  2. Большинству параллельных приложений требуется некоторая синхронизация между потоками для каждого запроса. Из-за этого происходит дорогостоящее переключение контекста между потоками ОС.

Возможным решением таких проблем является использование асинхронных параллельных API . Распространенными примерами являются CompletableFuture и RxJava . При условии, что такие API не блокируют поток ядра, это дает приложению более тонкую конструкцию параллелизма поверх потоков Java .

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

4. Задачи и планировщики

Любая реализация потока, будь то легкая или тяжелая, зависит от двух конструкций:

  1. Задача (также известная как продолжение) – последовательность инструкций, которая может приостановить себя для какой-либо операции блокировки
  2. Планировщик – Для назначения продолжения процессору и переназначения процессора из приостановленного продолжения

В настоящее время Java полагается на реализации ОС как для продолжения, так и для планировщика .

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

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

Этот тип планирования не является оптимальным для приложений Java, в частности .

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

Project Loom предлагает решить эту проблему с помощью потоков пользовательского режима, которые полагаются на реализацию Java runtime продолжений и планировщиков вместо реализации ОС .

5. Волокна

В последних прототипах в OpenJDK новый класс с именем Fiber вводится в библиотеку вместе с классом Thread .

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

  1. Fiber будет заключать любую задачу во внутреннее продолжение пользовательского режима. Это позволит задаче приостанавливаться и возобновляться в среде выполнения Java вместо ядра
  2. Будет использоваться подключаемый планировщик пользовательского режима (например, ForkJoinPool, )

Давайте подробно рассмотрим эти два пункта.

6. Продолжение

Продолжение (или сопрограмма) – это последовательность инструкций, которая может быть выполнена и возобновлена вызывающим абонентом на более позднем этапе.

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

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

Подобно потокам, Project Loom нацелен на поддержку вложенных волокон. Поскольку волокна полагаются на продолжения внутри, они также должны поддерживать вложенные продолжения. Чтобы лучше понять это, рассмотрим класс Continuation , который позволяет вложенность:

Continuation cont1 = new Continuation(() -> {
    Continuation cont2 = new Continuation(() -> {
        //do something
        suspend(SCOPE_CONT_2);
        suspend(SCOPE_CONT_1);
    });
});

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

Поскольку приостановка продолжения также потребует сохранения стека вызовов, целью project Loom также является добавление легкого извлечения стека при возобновлении продолжения.

7. Планировщик

Ранее мы обсуждали недостатки планировщика ОС при планировании связанных потоков на одном и том же процессоре.

Хотя целью Project Loom является разрешение подключаемых планировщиков с волокнами, ForkJoinPool в асинхронном режиме будет использоваться в качестве планировщика по умолчанию.

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

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

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

8. Заключение

В этой статье мы обсудили проблемы в текущей модели параллелизма Java и изменения, предложенные Project Loom .

При этом мы также определили задачи и планировщики и рассмотрели, как Fibers и ForkJoinPool могут обеспечить альтернативу Java с использованием потоков ядра.