«Если рабочий хочет хорошо выполнять свою работу, он должен сначала заточить свои инструменты» — Конфуций, «Аналитики Конфуция. Лу Лингун»
титульная страница > программирование > Исследование закрепления в механизме виртуальных потоков JVM

Исследование закрепления в механизме виртуальных потоков JVM

Опубликовано 8 ноября 2024 г.
Просматривать:277

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

Виртуальные потоки: упрощенный подход к параллелизму

Виртуальные потоки Java — это управляемые объекты, которые выполняются поверх потоков базовой операционной системы (потоков-носителей). Они обеспечивают более эффективный способ управления параллелизмом по сравнению с созданием множества потоков ОС, поскольку требуют меньших накладных расходов. JVM динамически сопоставляет виртуальные потоки с потоками-носителями, что позволяет лучше использовать ресурсы.

  • Управляется JVM: в отличие от потоков ОС, которые напрямую управляются операционной системой, виртуальные потоки создаются и планируются виртуальной машиной Java (JVM). Это обеспечивает более детальный контроль и оптимизацию в среде JVM.

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

  • Совместимость с существующим кодом: виртуальные потоки разработаны таким образом, чтобы их можно было легко интегрировать с существующим кодом Java. Их можно использовать вместе с традиционными потоками ОС и работать в рамках знакомых конструкций, таких как Executor и ExecutorService, для управления параллельным выполнением.

На рисунке ниже показана связь между виртуальными потоками и потоками платформы:

Exploring Pinning in JVM


Закрепление: когда виртуальный поток зависает

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

  • Синхронизированные блоки и методы: выполнение кода в синхронизированном блоке или методе приводит к закреплению. Это обеспечивает эксклюзивный доступ к общим ресурсам, предотвращая проблемы с повреждением данных.

Пример кода:

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

public class Main {

  public static void main(String[] args) throws InterruptedException {

    final Counter counter = new Counter();

    Runnable task = () -> {
      for (int i = 0; i 



В этом примере, когда виртуальный поток входит в синхронизированный блок, он закрепляется за своим потоком-носителем, но это не всегда так. Одного ключевого слова Synchronized в Java недостаточно, чтобы вызвать закрепление потоков в виртуальных потоках. Для закрепления потока в синхронизированном блоке должна быть точка блокировки, которая заставляет виртуальный поток инициировать парковку и в конечном итоге запрещает отключение от потока-носителя. Закрепление потоков может привести к снижению производительности, поскольку сведет на нет преимущества использования облегченных/виртуальных потоков.

Каждый раз, когда виртуальный поток сталкивается с точкой блокировки, его состояние меняется на PARKING. Этот переход состояния обозначается вызовом метода VirtualThread.park():

// JDK core code
void park() {
  assert Thread.currentThread() == this;
  // complete immediately if parking permit available or interrupted
  if (getAndSetParkPermit(false) || interrupted)
    return;
  // park the thread
  setState(PARKING);
  try {
    if (!yieldContinuation()) {
      // park on the carrier thread when pinned
      parkOnCarrierThread(false, 0);
    }
  } finally {
    assert (Thread.currentThread() == this) && (state() == RUNNING);
  }
}

Давайте рассмотрим пример кода, иллюстрирующий эту концепцию:

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

public class Main {

  public static void main(String[] args) {

    Counter counter = new Counter();

    Runnable task = () -> {
      for (int i = 0; i 



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

Мониторинг закрепления с помощью -Djdk.tracePinnedThreads=full

Флаг -Djdk.tracePinnedThreads=full — это аргумент запуска JVM, который предоставляет подробную информацию о трассировке закрепления виртуального потока. Если этот параметр включен, он регистрирует такие события, как:

  • Идентификатор виртуального потока, участвующего в закреплении
  • Идентификатор потока оператора связи, к которому прикреплен виртуальный поток
  • Трассировка стека, указывающая участок кода, вызвавший закрепление

Используйте этот флаг разумно только во время сеансов отладки, так как это приводит к снижению производительности.

  1. Скомпилируйте наш демо-код:

    javac Main.java
    
  2. Запустите скомпилированный код с флагом -Djdk.tracePinnedThreads=full:

    java -Djdk.tracePinnedThreads=full Main
    
  3. Наблюдайте за выводом консоли, который показывает подробную информацию о закреплении виртуального потока:

    Thread[#29,ForkJoinPool-1-worker-1,5,CarrierThreads]
    java.base/java.lang.VirtualThread$VThreadContinuation.onPinned(VirtualThread.java:183)
    java.base/jdk.internal.vm.Continuation.onPinned0(Continuation.java:393)
    java.base/java.lang.VirtualThread.parkNanos(VirtualThread.java:621)
    java.base/java.lang.VirtualThread.sleepNanos(VirtualThread.java:791)
    java.base/java.lang.Thread.sleep(Thread.java:507)
    Counter.increment(Main.java:38) 
    
    

Исправление закрепления с помощью реентерабельных блокировок

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

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

public class Main {

  public static void main(String[] args) {

    Counter counter = new Counter();

    Runnable task = () -> {
      for (int i = 0; i 



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

В заключение

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

Заявление о выпуске Эта статья воспроизведена по адресу: https://dev.to/yanev/exploring-pinning-in-jvms-virtual-thread-mechanism-5h13?1. Если есть какие-либо нарушения, свяжитесь с [email protected], чтобы удалить их.
Последний учебник Более>

Изучайте китайский

Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.

Copyright© 2022 湘ICP备2022001581号-3