[JUC 어드밴스드] 09. 락 업그레이드 안내

목차

1. 소개

2. 검토

2.1, 개체 헤더 및 메모리 레이아웃

2.2 4대 자물쇠 검토

3. 상태 전환

3.1 잠금 상태

3.1.1, 잠금 상태 없음

3.1.2 바이어스 잠금 상태

3.1.3, 경량 잠금 상태

3.1.4, 무거운 잠금 상태

3.2 상태 전이 조건

3.2.1, 잠금 없음 -> 바이어스 잠금

3.2.2, 바이어스 잠금 -> 잠금 없음

3.2.3, 바이어스 잠금 -> 경량 잠금

3.2.4, 경량 잠금 -> 중량 잠금

3.2.5 헤비급 잠금 -> 경량 잠금

4. 잠금 업그레이드 프로세스

5. 잠금을 다운그레이드할 수 있습니까?


1. 소개

동시 프로그래밍에서 잠금은 스레드 안전을 보장하는 중요한 메커니즘입니다. 그러나 높은 동시성 시나리오에서는 기존 잠금의 성능이 제한될 수 있습니다. 이 문제를 해결하기 위해 JUC는 런타임에 잠금 상태를 동적으로 조정하여 동시성 성능을 향상시키는 잠금 에스컬레이션 개념을 도입했습니다. 이전에 우리는 잠금이 없는, 편향된 잠금, 경량 잠금, 스핀 잠금 및 중량 잠금에 대한 지식을 소개했습니다. 이것은 실제로 우리가 자주 듣는 잠금 업그레이드인 JUC의 잠금 최적화로 인해 변환될 여러 상태입니다.

2. 검토

2.1, 개체 헤더 및 메모리 레이아웃

이 지식이 잘 이해되지 않으면 "[JUC Advanced] 03. Java 개체 헤더 및 메모리 레이아웃"을 읽을 수 있습니다. 다음은 간단한 리뷰입니다.

힙 메모리에 저장된 Java 객체의 레이아웃은 객체 헤더(Header), 인스턴스 데이터(Instance Data) 및 정렬 패딩(Padding)의 세 부분으로 나눌 수 있습니다. 개체 헤더(Header)에는 정보의 두 부분인 마크 필드(Mark Word)와 클래스 개체 포인터(Class Pointer)가 포함됩니다.

그 중 마크 필드(Mark Word)는 HashCode(해시 코드), GC 생성 연령, 잠금 상태 플래그, 스레드가 보유한 잠금, 바이어스된 스레드 ID, 바이어스된 타임스탬프와 같은 개체 자체의 런타임 데이터를 저장하는 데 사용됩니다. , 등. 클래스 객체 포인터(클래스 포인터)는 해당 유형 메타데이터에 대한 객체의 포인터이며 JVM은 이 포인터를 사용하여 객체가 인스턴스인 클래스를 결정합니다.

개체가 다양한 수준의 잠금을 보유하는 경우 마크 필드(마크 워드)는 관련 플래그 비트, 스레드 ID 및 기타 정보를 저장합니다.

2.2 4대 자물쇠 검토

이전에 여러 잠금에 대한 지식을 자세히 소개했습니다. 다음은 여러 잠금의 관련 특성에 대한 요약입니다.

잠금 유형

특성

자연

원칙

이점

결점

사용되는 장면

성능 오버헤드

자물쇠가 없다

비 차단, 동기화 없음

CAS를 통한 원자적 작동

원자 연산을 사용한 동시성 제어

비차단, 스레드 차단 및 전환의 오버헤드 방지

회전 대기는 CPU 리소스를 소비합니다.

높은 동시성 및 낮은 경합

낮음, 원자적 작업의 성능 손실만 포함

바이어스 잠금

단일 스레드에 적합

스레드 ID로 소유자 식별

처음으로 잠금을 획득할 때 잠금 개체의 마크 워드에 스레드 ID를 기록합니다.

다중 스레드 경쟁을 피하고 단일 스레드 실행 경로를 가속화합니다.

편향된 잠금은 여러 스레드가 경쟁할 때 취소되어 추가 오버헤드가 발생합니다.

잠금을 자주 획득하는 단일 스레드

낮음, 스레드 ID 비교 및 ​​쓰기 작업만 포함

경량 자물쇠

회전 대기

CAS 및 스핀으로 구현

편향된 잠금이 취소되거나 다중 스레드 경쟁이 발생하면 CAS를 사용하여 Mark Word를 대체합니다.

단기 잠금 경쟁에 적합한 스레드 차단 및 전환 오버헤드 감소

회전 대기는 CPU 리소스를 소비합니다.

단기 잠금 경합

보통, CAS 작업 및 회전 대기 포함

헤비급 자물쇠

차단하다

운영 체제 뮤텍스 사용

스레드 경쟁이 치열할 때는 운영 체제에서 제공하는 상호 배제 메커니즘을 사용하십시오.

다중 스레드 경쟁을 효과적으로 해결하고 데이터 보안 및 정확성을 보장할 수 있습니다.

스레드 차단 및 전환이 필요하고 오버헤드가 큽니다.

데이터 보안 및 정확성을 보장하기 위한 장기 잠금 경쟁

높음, 스레드 차단, 스위칭 및 OS 스케줄링 포함

성능 오버헤드 측면에서 보면 잠금 없음 ≤ 편향된 잠금 ≤ 경량 잠금 ≤ 중량 잠금입니다. 어느 정도 프로그래밍 경험이 있는 경우 업그레이드 프로세스가 필연적으로 성능 오버헤드에 영향을 미치므로 성능 오버헤드 분포에 따라 잠금 업그레이드(상태 전환) 프로세스를 추론할 수 있는지 여부를 인식해야 합니다. 대답은 '예'입니다.

3. 상태 전환

3.1 잠금 상태

3.1.1, 잠금 상태 없음

잠금 해제 상태에서 스레드는 잠금 제한 및 경쟁 없이 공유 리소스에 자유롭게 액세스할 수 있습니다. 여러 스레드가 동일한 공유 리소스에 동시에 액세스할 때 데이터 경합 및 스레드 안전 문제가 발생할 수 있습니다.

3.1.2 바이어스 잠금 상태

하나의 스레드만 동기화된 코드 블록에 액세스하는 경우 JVM은 개체를 편향된 잠금 상태로 표시합니다. 바이어스 잠금의 목적은 경쟁이 없을 때 잠금 오버헤드를 줄이는 것입니다. 스레드가 처음으로 동기화 코드 블록에 진입하면 JVM은 객체 헤더의 스레드 ID를 현재 스레드의 ID로 기록하고 객체 헤더의 상태를 바이어스된 잠금으로 설정합니다. 이후 스레드가 다시 동기화 코드 블록에 진입하면 별도의 동기화 작업 없이 바로 동기화 상태로 진입하게 된다.

3.1.3, 경량 잠금 상태

여러 스레드 간에 가벼운 경합이 있는 경우 JVM은 개체를 가벼운 잠금 상태로 표시합니다. 경량 잠금의 목적은 스레드 전환 및 잠금 취소의 오버헤드를 줄이는 전제에서 경합이 적은 동기화 메커니즘을 제공하는 것입니다.

3.1.4, 무거운 잠금 상태

여러 스레드 간에 치열한 경쟁이 있는 경우 JVM은 개체를 무거운 잠금 상태로 표시합니다. 무거운 잠금은 운영 체제에서 제공하는 뮤텍스를 사용하여 구현되며, 여기에는 스레드 차단 및 깨우기가 포함되며 운영 체제의 개입이 필요합니다.

3.2 상태 전이 조건

3.2.1, 잠금 없음 -> 바이어스 잠금

  • 스레드가 동기화된 블록에 처음으로 액세스하면 객체는 잠금 편향으로 표시되고 현재 스레드 ID를 기록합니다.
  • 전환 조건: 잠금 해제 상태의 객체가 다른 스레드에 의해 액세스됩니다.

3.2.2, 바이어스 잠금 -> 잠금 없음

  • 개체가 바이어스 잠금 상태에 있을 때 다른 스레드가 잠금을 획득하려고 하면 바이어스 잠금이 취소됩니다.
  • 전환 조건: 다른 스레드가 편향된 잠금을 획득하려고 시도합니다.

3.2.3, 바이어스 잠금 -> 경량 잠금

  • 스레드가 동기화된 코드 블록에 반복적으로 들어가지만 경쟁이 있는 경우 편향된 잠금이 경량 잠금으로 업그레이드됩니다.
  • 전환 조건: 동일한 개체에 편향된 잠금에 대한 경쟁이 있습니다.

3.2.4, 경량 잠금 -> 중량 잠금

  • 경량 잠금은 여러 스레드 간에 치열한 경쟁이 있을 때 중량 잠금으로 업그레이드됩니다.
  • 전환 조건: 경량 잠금 장치의 CAS 작업 경쟁이 실패합니다.

3.2.5 헤비급 잠금 -> 경량 잠금

  • 중량 잠금을 보유한 스레드가 잠금을 해제하면 잠금은 경량 잠금으로 다운그레이드를 시도합니다.
  • 전환 조건: 무거운 잠금을 잡고 있는 스레드가 잠금을 해제합니다.

4. 잠금 업그레이드 프로세스

无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁
  1. 바이어스 잠금 업그레이드: 스레드가 동기화된 블록에 액세스할 때 먼저 바이어스 잠금을 획득하려고 시도합니다. 현재 객체가 다른 스레드와 경쟁하지 않고 편향된 잠금을 보유한 스레드가 아직 활성 상태인 경우 현재 스레드는 잠금 에스컬레이션 없이 편향된 잠금을 직접 획득할 수 있습니다.
  2. 경량 잠금 업그레이드: 바이어스 잠금 획득에 실패하여 현재 개체에 대한 경쟁이 있음을 나타내는 경우 바이어스 잠금이 경량 잠금으로 업그레이드됩니다. 이때 JVM은 CAS 연산을 통해 객체 헤더의 잠금 표시를 스레드 스택의 잠금 레코드(Lock Record)에 대한 포인터로 변경하고 객체의 내용을 잠금 레코드에 복사합니다.
  3. 스핀 잠금 업그레이드: 경량 잠금 획득에 실패하는 경우, 즉 여러 스레드가 동일한 개체의 잠금을 위해 경쟁하면 경량 잠금이 스핀 잠금으로 업그레이드됩니다. 스핀 잠금은 스레드를 차단하지 않지만 스레드가 반복적으로 잠금을 획득하려고 시도하면서 바쁜 대기를 수행하도록 허용합니다. 이렇게 하면 스레드 전환으로 인한 성능 손실을 피할 수 있습니다.
  4. 헤비웨이트 잠금 업그레이드: 잠금을 획득하려는 스핀록의 횟수가 특정 임계값에 도달하거나 대기 시간이 특정 제한을 초과하면 스핀록이 헤비웨이트 잠금으로 업그레이드됩니다. 무거운 잠금은 스레드를 차단하고 잠금을 위해 경쟁하는 스레드를 대기 큐에 넣고 잠금이 해제된 후 깨어납니다.

일반 업그레이드 순서도:

구체적인 흐름도는 다음과 같습니다.

5. 잠금을 다운그레이드할 수 있습니까?

Java에서 잠금은 일반적으로 능동적으로 다운그레이드되지 않습니다. 즉, 잠금이 더 높은 수준의 잠금으로 업그레이드되면(예: 편향된 잠금에서 경량 잠금 또는 중량 잠금으로 업그레이드) 자동으로 다시 다운그레이드되지 않습니다. 낮은 수준의 잠금.

그러나 잠금이 해제되는 경우가 있습니다. 즉, 무거운 잠금이 해제되면 가벼운 잠금으로 다운그레이드될 수 있습니다. 이 다운그레이드는 Heavyweight Lock을 잡고 있는 스레드가 Lock을 해제한 후에 발생합니다. 잠금을 위해 경쟁하십시오.

다운그레이드 프로세스는 JVM에 의해 자동으로 처리되며 특정 트리거 조건 및 전략은 JVM 구현에 따라 다를 수 있습니다. 일반적으로 무거운 잠금을 해제하는 스레드는 다른 스레드가 동일한 잠금에 대해 경합하고 있지 않음을 감지하면 잠금을 경량 잠금으로 다운그레이드합니다.

잠금 다운그레이드가 모든 경우에 발생하는 것은 아니며 시스템의 경쟁 조건과 JVM의 특정 구현에 따라 다릅니다. 실제 응용에서 자물쇠의 성능 저하를 직접 제어할 수 없기 때문에 자물쇠를 선택하고 사용할 때 특정 상황과 요구에 따라 종합적으로 고려하고 자물쇠의 수준과 성능을 평가해야 합니다.

 

Guess you like

Origin blog.csdn.net/p793049488/article/details/131513512