От Java к JVM к спящему потоку ОС

Java задняя часть JVM C++
От Java к JVM к спящему потоку ОС

предисловие

Иногда необходимо перевести поток в спящее состояние в Java, тогда мы обычно переходимThread.sleepПереведите поток в спящий режим и посмотрите, что делает выполнение оператора в JVM.

Простой пример

Вот простой пример, который усыпляет основной поток на 5 секунд.

public class TestSleep {

	public static void main(String[] args) {
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

Потоки в JVM

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

В JVM некоторые классы Thread также определены в C++, структура их наследования следующая: для потока уровня Java к уровню JVM основными родственными являются java.lang.Thread, JavaThread и OSThread уровня Java.

  • java.lang.Thread принадлежит объекту потока уровня Java.Каждый объект уровня Java будет представлен как oop в JVM, поэтому он также будет генерировать oop в JVM.
  • Thread — это базовый класс потока, определенный в C++.За исключением класса OSThread, в качестве базового класса других потоков он содержит указатель на объект OSThread.
  • JavaThread — это класс потока, определенный в C++.Объект потока, который мы создаем на уровне Java, будет представлен объектом JavaThread, который содержит указатель на цикл потока.
  • OSThread – это поток, определенный в C++. Он не образует отношения наследования с другими потоками. Это унифицированная абстракция JVM для потоков различных операционных систем. Он поддерживает дескрипторы потоков операционной системы и используется для получения потоков операционной системы.
--Thread
	--JavaThread
		--CodeCacheSweeperThread
		--CompilerThread
		--JvmtiAgentThread
		--ServiceThread
	--NamedThread
		--ConcurrentGCThread
		--VMThread
		--WorkerThread
			--AbstractGangWorker
			--GCTaskThread
	--WatcherThread
--OSThread

метод сна

В классе Threadsleepявляется статическим и собственным методом.

public static native void sleep(long millis) throws InterruptedException;

Thread.c

Собственные методы, объявленные на уровне Java, соответственно реализованы в Thread.c,sleepэто метод, зарегистрированный в JVM, который совпадает с методом в JVMJVM_SleepФункция связана, поэтому логика реализации находится вJVM_Sleepв функции. Логика такова:

  • JVMWrapper("JVM_Sleep")для отладки.
  • Время сна не может быть отрицательным.
  • был прерван.
  • JavaThreadSleepState jtss(thread)Он используется для изменения состояния потока и создания некоторой статистики.Когда спящий режим закончится, он будет изменен обратно в состояние потока и изменен в деструкторе JavaThreadSleepState.
  • Если время сна равно 0, согласноConvertSleepToYieldВыполните другую обработку, это указывает, следует ли превратить операцию сна в операцию выхода. звоните отдельноos::naked_yieldиos::sleepОбработка инкапсулирует реализацию вызова различных операционных систем и берет Windows в качестве примера, чтобы увидеть соответствующую реализацию.
  • пройти черезthread->osthread()->get_state()Получите объект OSThread и установите его состояние вSLEEPINGВернитесь в исходное состояние после окончания сна.
  • Если время сна больше 0, сделайте то же самое, но с поддержкой прерываний.
  • Отправить событие, конец.
JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
  JVMWrapper("JVM_Sleep");

  if (millis < 0) {
    THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
  }

  if (Thread::is_interrupted (THREAD, true) && !HAS_PENDING_EXCEPTION) {
    THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
  }

  JavaThreadSleepState jtss(thread);

  HOTSPOT_THREAD_SLEEP_BEGIN(millis);

  EventThreadSleep event;

  if (millis == 0) {
    if (ConvertSleepToYield) {
      os::naked_yield();
    } else {
      ThreadState old_state = thread->osthread()->get_state();
      thread->osthread()->set_state(SLEEPING);
      os::sleep(thread, MinSleepInterval, false);
      thread->osthread()->set_state(old_state);
    }
  } else {
    ThreadState old_state = thread->osthread()->get_state();
    thread->osthread()->set_state(SLEEPING);
    if (os::sleep(thread, millis, true) == OS_INTRPT) {
      if (!HAS_PENDING_EXCEPTION) {
        if (event.should_commit()) {
          event.set_time(millis);
          event.commit();
        }
        HOTSPOT_THREAD_SLEEP_END(1);
        THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
      }
    }
    thread->osthread()->set_state(old_state);
  }
  if (event.should_commit()) {
    event.set_time(millis);
    event.commit();
  }
  HOTSPOT_THREAD_SLEEP_END(0);
JVM_END

os::naked_yield

naked_yieldРеализация функции очень проста, достаточно вызвать ее напрямуюSwitchToThreadсистемная функция. Эта функция позволяет системе видеть, есть ли другие потоки, которым срочно нужен ЦП, отдавать ЦП другим потокам и немедленно возвращаться, если других потоков нет.

void os::naked_yield() {
  SwitchToThread();
}

os::sleep

  • Получите максимальный предельный размер лимита.
  • Если он превышает лимит, он преобразуется в несколько рекурсивных вызовов путем вычитанияsleepфункция.
  • Получите объект OSThread, а затем установите состояние ожидания потока через OSThreadWaitState, а операции модификации реализуются в конструкторе и деструкторе соответственно.
  • Различные реализации выполняются в зависимости от того, поддерживаются ли прерывания, и они вызываются напрямую без прерываний.Sleepсистемные функции для реализации.
  • Если вы хотите поддерживать прерывания, выполните следующую обработку.
  • ThreadBlockInVM в основном проверяет, нужно ли текущему потоку войти в точку сохранения, и мы подробно рассмотрим это позже.
  • Затем в основном кWaitForMultipleObjectsСистемная функция, ожидающая заданное количество миллисекунд для указанного объекта. Если объект не получает никакого сигнала в процессе ожидания, он вернется через указанное количество миллисекунд.WAIT_TIMEOUT, если объект получает сигнал в процессе ожидания, ожидание отменяется заранее, а возвращаемое значение равноOS_INTRPT, что означает, что он был прерван.
int os::sleep(Thread* thread, jlong ms, bool interruptable) {
  jlong limit = (jlong) MAXDWORD;

  while (ms > limit) {
    int res;
    if ((res = sleep(thread, limit, interruptable)) != OS_TIMEOUT) {
      return res;
    }
    ms -= limit;
  }

  assert(thread == Thread::current(), "thread consistency check");
  OSThread* osthread = thread->osthread();
  OSThreadWaitState osts(osthread, false /* not Object.wait() */);
  int result;
  if (interruptable) {
    assert(thread->is_Java_thread(), "must be java thread");
    JavaThread *jt = (JavaThread *) thread;
    ThreadBlockInVM tbivm(jt);

    jt->set_suspend_equivalent();
    HANDLE events[1];
    events[0] = osthread->interrupt_event();
    HighResolutionInterval *phri=NULL;
    if (!ForceTimeHighResolution) {
      phri = new HighResolutionInterval(ms);
    }
    if (WaitForMultipleObjects(1, events, FALSE, (DWORD)ms) == WAIT_TIMEOUT) {
      result = OS_TIMEOUT;
    } else {
      ResetEvent(osthread->interrupt_event());
      osthread->set_interrupted(false);
      result = OS_INTRPT;
    }
    delete phri; 
    jt->check_and_wait_while_suspended();
  } else {
    assert(!thread->is_Java_thread(), "must not be java thread");
    Sleep((long) ms);
    result = OS_TIMEOUT;
  }
  return result;
}

ThreadBlockInVM

Как упоминалось ранее, ThreadBlockInVM будет проверять, нужно ли текущему потоку заходить в safepoint, его основная логика такова:

  • Сначала установите состояние потока Java, увеличьте состояние на единицу, на_thread_in_vm = 6стать_thread_in_vm_trans = 7, от «запуска собственного кода виртуальной машины» до «соответствующего переходного состояния».
  • os::is_MP()Он используется для определения того, является ли компьютерная система многоядерной системой.В случае многоядерности требуется обработка барьера памяти, чтобы позволить каждому потоку синхронизировать состояние в реальном времени.
  • Есть два способа барьера памяти, один из нихrderAccess::fence(), его реализация непосредственно реализуется инструкциями ЦП, а инструкции по сборке__asm__ volatile ("lock; addl $0,0(%%rsp)" : : : "cc", "memory");, этот способ дороже. И еще одинInterfaceSupport::serialize_memory, реализованный с помощью моделирования JVM, что является более эффективным.
  • перечислитьSafepointSynchronize::blockПопытайтесь заблокировать эту точку безопасности.
  • Установите состояние потока Java в_thread_blocked, т.е. блокировка.
static inline void transition_and_fence(JavaThread *thread, JavaThreadState from, JavaThreadState to) {
    assert(thread->thread_state() == from, "coming from wrong thread state");
    assert((from & 1) == 0 && (to & 1) == 0, "odd numbers are transitions states");
    thread->set_thread_state((JavaThreadState)(from + 1));

    if (os::is_MP()) {
      if (UseMembar) {
        OrderAccess::fence();
      } else {
        // Must use this rather than serialization page in particular on Windows
        InterfaceSupport::serialize_memory(thread);
      }
    }

    if (SafepointSynchronize::do_call_back()) {
      SafepointSynchronize::block(thread);
    }
    thread->set_thread_state(to);

    CHECK_UNHANDLED_OOPS_ONLY(thread->clear_unhandled_oops();)
  }

------------- Рекомендуем прочитать ------------

Краткое изложение моих проектов с открытым исходным кодом (машинное и глубокое обучение, НЛП, сетевой ввод-вывод, AIML, протокол mysql, чат-бот)

Зачем писать «Анализ проектирования ядра Tomcat»

Резюме моей статьи за 2017 год — машинное обучение

Краткое изложение моих статей за 2017 год — Java и промежуточное ПО

Резюме моих статей 2017 года — глубокое обучение

Краткое изложение моих статей за 2017 год — исходный код JDK

Резюме моей статьи за 2017 год — обработка естественного языка

Резюме моих статей 2017 года — Java Concurrency


Поговори со мной, задай мне вопросы:

Добро пожаловать, чтобы следовать: