>  기사  >  Java  >  JAVA 가상 머신(JVM)에 대한 자세한 소개(8) - 효율적인 동시성

JAVA 가상 머신(JVM)에 대한 자세한 소개(8) - 효율적인 동시성

王林
王林앞으로
2019-08-24 17:25:482355검색

JAVA 가상 머신(JVM)에 대한 자세한 소개(8) - 효율적인 동시성

메모리 모델

메모리 모델은 특정 운영 프로토콜에 따라 특정 메모리나 캐시에 대한 읽기 및 쓰기 액세스 프로세스를 추상화한 것입니다. 주요 목표는 프로그램의 다양한 변수에 대한 액세스 규칙을 정의하는 것입니다.

주 메모리와 작업 메모리

JAVA 가상 머신(JVM)에 대한 자세한 소개(8) - 효율적인 동시성

모든 변수는 자체 작업 메모리에 저장되며 작업 메모리는 스레드 A에 의해 점유됩니다. 메인 메모리 복사 사용된 변수의 복사와 스레드가 변수에 값을 읽고 할당하는 등의 작업은 작업 메모리에서 수행되어야 하며, 메인 메모리에 있는 변수는 직접 읽을 수 없습니다.

메모리 간 상호 작용

주 메모리에서 작업 메모리로 복사: 읽기 및 로드 작업을 순차적으로 수행합니다.
작업 메모리는 주 메모리와 동기화되어 저장 및 쓰기 작업을 수행합니다.

휘발성의 특징

Volatile은 동기화와 동일한 기능을 가지고 있지만 동기화보다 가볍습니다. 주요 기능은 다음과 같습니다.

이 변수의 가시성을 모든 스레드에 보장합니다

무슨 뜻인가요? 이는 스레드가 이 변수의 값을 수정하면 새 값이 즉시 다른 스레드에 알려짐을 의미합니다. 일반 변수는 이를 수행할 수 없으며 스레드 간 일반 변수 값의 전송은 주 메모리를 통해 완료되어야 합니다. 예를 들어 스레드 A는 일반 변수의 값을 수정한 다음 이를 다른 스레드에 다시 씁니다. B는 스레드 B에 있습니다. A가 다시 쓰기를 완료하고 주 메모리에서 읽은 후 새 변수 값이 스레드 B에 표시됩니다.

명령어 재정렬 최적화를 비활성화합니다

명령어 재정렬은 프로그램의 동시 실행을 방해하기 때문입니다.

멀티스레딩

왜 멀티스레딩이 필요한가요?

컴퓨터의 컴퓨팅 속도와 저장 및 통신 하위 시스템의 속도 사이의 격차가 너무 큽니다. 디스크 I/O, 네트워크 통신 및 데이터베이스 액세스에 많은 시간이 소요됩니다. 멀티스레딩을 사용하면 CPU를 더 잘 활용할 수 있습니다.

동시 적용 시나리오는 무엇입니까?

컴퓨터 프로세서를 최대한 활용하세요

서버는 동시에 여러 클라이언트에 서비스를 제공합니다.

프로세서 내부의 컴퓨팅 장치를 최대한 활용하는 방법은 무엇입니까?

캐시 레이어 추가

작업을 빠르게 수행할 수 있도록 작업에 필요한 데이터를 캐시에 복사합니다. 작업이 완료되면 캐시에서 메모리로 다시 동기화되므로 프로세서는 느린 메모리 읽기 및 쓰기를 기다릴 필요가 없습니다. 그러나 고려해야 할 문제가 있습니다. 캐시 일관성을 보장하는 방법입니다.

JAVA 가상 머신(JVM)에 대한 자세한 소개(8) - 효율적인 동시성

입력 코드의 비순차적 실행 최적화

스레드 구현 방법

커널 스레드 사용

커널 스레드는 운영 체제 커널에서 직접 지원하는 스레드입니다.

사용자 스레드를 사용하여 구현

사용자 스레드의 생성, 동기화, 파괴 및 예약은 커널의 도움 없이 사용자 모드에서 완전히 완료되며 커널은 스레드의 존재를 인식하지 못합니다. 이 구현은 거의 사용되지 않습니다.

하이브리드 구현을 위해 사용자 스레드 플러스 경량 사용

함께 병합

스레드 예약

스레드 예약은 시스템이 프로세서 사용 권한을 스레드에 할당하는 프로세스를 나타냅니다. 협업형과 선점형의 두 가지 주요 유형이 있습니다.

협력

스레드의 실행 시간은 스레드 자체에 의해 제어됩니다. 스레드가 작업 실행을 마치면 시스템에 다른 스레드로 전환하도록 적극적으로 알립니다.
구현이 간단하고 스레드 동기화 문제가 없다는 것이 장점입니다. 단점은 스레드 프로그래밍에 문제가 있고 시스템에 스레드를 전환하라는 지시가 없으면 프로그램이 항상 차단되어 시스템이 쉽게 충돌할 수 있다는 것입니다.

Preemptive

스레드에는 시스템에 의해 실행 시간이 할당되며 스레드 전환은 자체적으로 결정되지 않습니다. 이것은 Java에서 사용하는 스레드 스케줄링 방법입니다.

스레드 안전성

여러 스레드가 객체에 액세스할 때 런타임 환경에서 이 스레드의 예약 및 대체 실행을 고려하지 않으면 추가 동기화를 수행하거나 호출자에 대해 아무 작업도 수행할 필요가 없습니다. 다른 조정된 작업과 이 개체에 대한 호출이 올바른 결과를 얻을 수 있으면 이 개체는 안전합니다.

공유 데이터 분류


Immutable

Immutable 공유 데이터는 스레드로부터 안전해야 하는 final로 수정된 데이터입니다. 공유 데이터가 기본형 변수라면 정의할 때 final 키워드를 사용하면 됩니다.

공유 데이터가 객체인 경우 객체의 동작이 상태에 영향을 주지 않아야 합니다. 객체의 상태가 있는 모든 변수를 final로 선언할 수 있습니다. 예를 들어, String 클래스는 불변 클래스입니다

완전히 스레드로부터 안전합니다

Java API에서 자신을 스레드로부터 안전한 클래스로 표시하세요. 대부분은 스레드로부터 완전히 안전하지 않습니다. 예를 들어 Vector는 스레드로부터 안전한 컬렉션이며 모든 메서드가 동기화되도록 수정되었지만 다중 스레드 환경에서는 여전히 동기화되지 않습니다.

상대적 스레드 안전성

상대적 스레드 안전성은 일반적으로 이 객체에 대한 별도의 작업이 스레드로부터 안전하다는 것을 보장할 수 있습니다. 그러나 연속 호출의 일부 특정 시퀀스의 경우 호출의 정확성을 보장하기 위해 호출 측에서 추가 동기화 수단을 사용해야 할 수도 있습니다.
대부분의 스레드 안전 클래스가 이 유형에 속합니다.

스레드 호환

객체 자체는 선형적으로 안전하지 않지만 호출 측에서 동기화 수단을 올바르게 사용하면 객체가 동시 환경에서 안전하게 사용될 수 있음을 보장할 수 있습니다. 스레드로부터 안전하지 않은 대부분의 클래스가 이 범주에 속합니다.

스레드 반대

어쨌든 System.setIn(), System.SetOut() 등 멀티스레드 환경에서는 동시에 사용할 수 없습니다. 하나는 입력을 수정하고 다른 하나는 출력을 수정합니다. 두 가지를 "교대로" 수행할 수는 없습니다.

구현 방법

방법 1: 상호 배타적 동기화 - 비관적 동시성 전략

원칙은 다음과 같습니다. 이 키워드는 컴파일되었으며 두 개의 바이트코드 명령어가 있습니다. , monitorenter 및 monitorexit는 동기화 블록 전후에 형성됩니다. monitorenter 명령이 실행되면 프로그램은 객체의 잠금을 획득하려고 시도합니다. 획득할 수 있으면 잠금 카운터는 +1이 되며, 이에 따라 monitorexit가 실행될 때 잠금 카운터는 -1이 됩니다. 카운터가 0에 도달하면 잠금이 해제됩니다.

특징은 다음과 같습니다. 동일한 스레드에 대해 재진입이 가능합니다. 동기화 블록은 입력된 스레드가 실행을 완료하기 전에 다른 후속 스레드가 진입하는 것을 차단합니다.

선택 시나리오는 다음과 같습니다. 무거우므로 꼭 필요한 경우에만 사용하세요.

(2) ReentrantLock

이 재진입 잠금은 JUC(java.util.concurrent) 패키지 아래의 클래스입니다. 고급 기능에는 대기 중단, 공정한 잠금 구현, 잠금을 여러 조건에 바인딩할 수 있는 기능이 포함됩니다.

방법 2: 비차단 동기화 - 낙관적 동시성 전략

먼저 수행 , 공유 데이터에 대해 경쟁하는 다른 스레드가 없으면 작업이 성공합니다. 공유 데이터에 대한 경합이 발생하고 충돌이 발생하면 다른 보상 조치가 취해집니다.

방법 3: 동기화 계획 없음

방법에 데이터 공유가 포함되지 않으면 동기화 조치가 필요하지 않습니다. 반복 가능한 코드 및 스레드 로컬 저장소 등이 있습니다.

(1) 재진입 코드

메서드가 예측 가능한 결과를 반환하는 경우 동일한 데이터가 입력되는 한 동일한 결과를 반환하면 재진입을 충족합니다. 요구 사항.

(2) 스레드 로컬 저장소

코드의 일부에 필요한 데이터를 다른 코드와 공유해야 하고, 해당 데이터를 공유하는 코드가 동일한 코드에서 실행되는 경우 이와 같이 공유 데이터의 가시 범위를 하나의 스레드로 제한할 수 있으므로 동기화 없이 스레드 간에 데이터 경합 문제가 발생하지 않습니다.

잠금 최적화

적응형 회전

#🎜 🎜#JAVA 스레드를 차단하거나 깨우려면 운영 체제가 CPU 상태를 완료하도록 전환해야 하기 때문에 이 상태 전환에는 프로세서 시간이 필요합니다. 동기화 코드 블록의 내용이 너무 단순하면 상태 전환이 사용자 코드의 실행 시간보다 오래 걸릴 가능성이 높습니다.

이 문제를 해결하기 위해 잠금을 요청하는 후속 스레드가 "잠시만 기다리도록" 하고 바쁜 루프를 실행한 후 회전하도록 할 수 있습니다. 현재 프로세서 실행 시간은 포기되지 않습니다. 스핀이 제한된 횟수를 초과했지만 여전히 잠금을 성공적으로 획득하지 못한 경우 전통적인 방법을 사용하여 스레드를 일시 중단합니다.

그럼 적응형 스핀이란 무엇일까요?

spin wait가 방금 성공적으로 잠금을 획득한 경우 가상 머신은 이번에는 스핀으로 잠금을 획득할 확률이 상당히 높다고 생각할 것입니다. , 스핀 대기가 상대적으로 더 오래 지속됩니다. 반대로, 스핀이 좀처럼 오버록 획득에 성공하지 못하는 경우에는 스핀과정을 생략할 수도 있다.

잠금 제거

은 가상 머신 JIT(Just-In-Time) 컴파일러가 실행 중일 때 일부 코드에서 동기화가 필요하지만 공유 데이터 경쟁이 없을 것으로 감지되는 잠금을 제거한다는 의미입니다.

잠금 거칠기

일련의 연속 작업이 동일한 개체를 반복적으로 잠그고 잠금 해제하거나 루프 본문에서 잠금 작업이 발생하면 스레드 경쟁이 없더라도 빈번한 상호 배제 동기화 작업으로 인해 불필요한 성능 손실이 발생합니다.
가상 머신이 일련의 조각화된 작업이 모두 동일한 개체를 잠그는 것을 감지하면 잠금 동기화 범위를 전체 작업 시퀀스 외부로 축소하여 한 번만 잠그면 됩니다.

경량 잠금

은 다중 스레드 경쟁 없이 운영 체제 뮤텍스를 사용하는 기존의 중량 잠금으로 인한 성능 손실을 줄입니다.
적용 가능한 시나리오: 실제 경쟁이 없으며 여러 스레드가 잠금을 교대로 사용하는 단기 잠금 경쟁이 허용됩니다.

Biased lock

Biased lock은 경쟁이 없고 단 하나의 스레드만 잠금을 사용할 때 경량 잠금의 성능 소모를 줄이기 위해 사용됩니다. 경량 잠금은 잠금을 적용하고 해제할 때마다 최소 하나의 CAS가 필요하지만 바이어스 잠금은 초기화 중에 CAS가 하나만 필요합니다.
적용 가능한 시나리오: 실제 경쟁은 없으며 잠금을 신청한 첫 번째 스레드만 향후 잠금을 사용하게 됩니다.

위 내용은 JAVA 가상 머신의 효율적인 동시성에 대한 자세한 소개입니다. 관련 질문이 더 있으면 PHP 중국어 웹사이트를 방문하세요. JAVA Video Tutorial

위 내용은 JAVA 가상 머신(JVM)에 대한 자세한 소개(8) - 효율적인 동시성의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 csdn.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제