"일꾼이 일을 잘하려면 먼저 도구를 갈고 닦아야 한다." - 공자, 『논어』.
첫 장 > 프로그램 작성 > JVM의 가상 스레드 메커니즘에서 고정 탐색

JVM의 가상 스레드 메커니즘에서 고정 탐색

2024-11-08에 게시됨
검색:193

Java의 가상 스레드는 기존 OS 스레드에 대한 가벼운 대안을 제공하여 효율적인 동시성 관리를 가능하게 합니다. 그러나 최적의 성능을 위해서는 이들의 행동을 이해하는 것이 중요합니다. 이 블로그 게시물에서는 가상 스레드 실행에 영향을 미칠 수 있는 시나리오인 고정에 대해 자세히 알아보고 이를 모니터링하고 해결하는 기술을 살펴봅니다.

가상 스레드: 경량 동시성 접근 방식

Java의 가상 스레드는 기본 운영 체제 스레드(캐리어 스레드) 위에서 실행되는 관리되는 엔터티입니다. 오버헤드가 낮기 때문에 수많은 OS 스레드를 생성하는 것보다 동시성을 처리하는 더 효율적인 방법을 제공합니다. JVM은 가상 스레드를 캐리어 스레드에 동적으로 매핑하여 리소스 활용도를 향상시킵니다.

  • JVM에 의해 관리됨: 운영 체제에 의해 직접 관리되는 OS 스레드와 달리 가상 스레드는 JVM(Java Virtual Machine)에 의해 생성되고 예약됩니다. 이를 통해 JVM 환경 내에서 더욱 세밀한 제어 및 최적화가 가능해졌습니다.

  • 오버헤드 감소: 가상 스레드를 생성하고 관리하면 OS 스레드에 비해 오버헤드가 상당히 낮아집니다. 이는 JVM이 더 적은 수의 기본 OS 스레드를 활용하여 더 큰 가상 스레드 풀을 효율적으로 관리할 수 있기 때문입니다.

  • 기존 코드와의 호환성: 가상 스레드는 기존 Java 코드와 원활하게 통합되도록 설계되었습니다. 기존 OS 스레드와 함께 사용할 수 있으며 동시 관리를 위해 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 



이 예에서 가상 스레드가 동기화된 블록에 들어가면 캐리어 스레드에 고정되지만 항상 그런 것은 아닙니다. 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 시작 인수입니다. 활성화되면 다음과 같은 이벤트가 기록됩니다.

  • 고정과 관련된 가상 스레드 ID
  • 가상 스레드가 고정된 캐리어 스레드 ID
  • 고정을 유발하는 코드 섹션을 나타내는 스택 추적

이 플래그는 성능 오버헤드가 발생하므로 디버깅 세션 중에만 신중하게 사용하십시오.

  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의 가상 스레드는 언어의 진화와 기능에 대한 증거입니다. 이는 기존 OS 스레드에 대한 새롭고 가벼운 대안을 제공하여 효율적인 동시성 관리를 위한 다리를 제공합니다. 스레드 고정과 같은 핵심 개념을 깊이 파고들고 이해하는 데 시간을 투자하면 개발자는 이러한 경량 스레드의 잠재력을 최대한 활용할 수 있는 노하우를 얻을 수 있습니다. 이러한 지식은 개발자가 향후 기능을 활용할 수 있도록 준비할 뿐만 아니라 현재 프로젝트에서 복잡한 동시성 제어 문제를 보다 효과적으로 해결할 수 있도록 지원합니다.

릴리스 선언문 이 기사는 https://dev.to/yanev/exploring-pinning-in-jvms-virtual-thread-mechanism-5h13?1에서 복제됩니다.1 침해 내용이 있는 경우, [email protected]에 연락하여 삭제하시기 바랍니다.
최신 튜토리얼 더>

부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.

Copyright© 2022 湘ICP备2022001581号-3