일일 질문 어떻게 정확한 이중 검사 잠금을 실현할 수 있습니까
연유
우리 프로그램에서 때때로 비용이 많이 드는 대상이 만들어지면 미리 만들어지지 않고 실제적으로 사용할 때만 만들어진다.바로 기본 아래의 이 문법이다.
public class UnsafeLazyInitialization {
private static Instance instance;
public static Instance getInstance() {
if (instance == null) // 1:A
instance = new Instance(); // 2:B
return instance;
}
}
위의 이 쓰기 방법은 A 라인이 1까지 실행되었을 때 B 라인이 2를 막 실행했다.그럼 이때 A라인은 instance가 초기화되지 않은 것을 볼 수 있습니다.그러면 발생하는 메모리 가시성 문제.
노동 인민의 지혜는 무궁무진한 "synchronized를 더하면 끝이다"~~
public class UnsafeLazyInitialization {
private static Instance instance;
public synchronized static Instance getInstance() {
if (instance == null)
instance = new Instance();
return instance;
}
}
그러나 이 맞춤법은 병렬 라인이 많으면 자물쇠의 빈번한 경쟁으로 성능 비용이 많이 든다.초기의 JVM에서 synchronized의 성능이 비교적 떨어졌기 때문에 노동자들의 지혜는 또 뜨거운 빛을 발하여 JVM의 눈을 멀게 하였다.
이중점검 잠금(Double-Checked Locking) 횡공출세!!!
public class DoubleCheckedLocking { // 1
private static Instance instance; // 2
public static Instance getInstance() { // 3
if (instance == null) { // 4:
synchronized (DoubleCheckedLocking.class) { // 5:
if (instance == null) // 6:
instance = new Instance(); // 7:
} // 8
} // 9
return instance; // 10
} // 11
}
위의 이 문법에 따라 첫 번째 검사를 할 때 대상이null이 아니면 대상의 실례를 직접 되돌려줍니다.대상이null이면 실례화 대상을 잠그십시오.다중 스레드 아래에서 단 하나의 스레드만 실례화 대상을 제거할 수 있다는 것을 보장한다.
완벽해 보이지만 오래된 문제입니다. 라인 A가 instance가null이 아니라고 판단한 후에 얻은 instance는 아직 초기화되지 않았을 수도 있습니다.
그렇다면 왜 이런 상황이 벌어졌을까?
의 근원
한 대상의 창설은 대략 세 가지 단계로 요약할 수 있다. TODO: 이 세 가지 단계가 아니라 뒤에 문장을 전문적으로 보충할 것이다.
자바 언어 규범 [The 자바 Language Specification]에서 모든 스레드는 자바 프로그램을 실행할 때 intra-thread semantics(스레드 내 의미)를 준수해야 하며, intra-thread semantics는 한 라인에서 실행 결과를 바꾸지 않는 정렬을 허용합니다.왜 허락해야 하는지, 프로그램의 실행 성능을 향상시킬 수 있으니까~
Mon 06 Mon 13 Mon 20 A1: 할당된 개체의 메모리 공간 A3: 개체가 메모리 공간 B1을 가리키도록 설정합니다. 개체가null B2인지 여부를 판단합니다. 개체가null이 아닙니다. 개체 A2: 초기화 개체 A4: 개체 t1 t2 t3 t4 t5 t6 개체 중 하나가 버그를 발생시키는 논리적 실행 순서입니다.
위의 시퀀스에서 A2와 A3은 순서를 재정리했지만 Java 메모리 모델의 intra-thread semantics에 따라 A4가 A2보다 앞서도록 합니다.오직 이렇게 해야만 A라인의 집행 결과가 바뀌지 않을 것이다.그러나 A2, A3의 리셋으로 인해 B1이 instance가null로 판단될 때false로 되돌아온다. 왜냐하면 이때 A3이 이미 실행되었기 때문이다.그래서 B2가 대상을 직접 방문할 때 문제가 생길 수 있습니다. 왜냐하면 이 대상은 아직 초기화되지 않았기 때문입니다.
솔루션
문제가 생기는 원인을 알게 되면 그에 상응하는 해결 방향도 순조롭게 나올 것이다
public class DoubleCheckedLocking { // 1
private volatile static Instance instance; // 2 : volatile
public static Instance getInstance() { // 3
if (instance == null) { // 4 --- A
synchronized (DoubleCheckedLocking.class) { // 5
if (instance == null) // 6
instance = new Instance(); // 7 --- B
}
}
return instance;
}
}
첫 번째 사고방식은 키워드volatile를 사용하여 실현할 수 있다.'일일일문'volatile가 무엇을 하는지volatile의 메모리 의미를 통해 알 수 있듯이 루틴 A가 네 번째 줄까지 실행될 때 루틴 B가 일곱 번째 줄의 대상에서 생성이 완료되지 않으면 루틴 B는 메인 메모리를 갱신하지 않습니다.그러면 라인 A는 instance의 메모리 가시성 문제가 나타나지 않을 것이다.......................................
public class InstanceFactory {
private static class InstanceHolder {
public static Instance instance = new Instance();
}
public static Instance getInstance() {
return InstanceHolder.instance ; // InstanceHolder
}
}
두 번째 사고방식은 유형 초기화의 사고방식을 빌려 실현할 수 있다.모든 종류가 초기화될 때 '자물쇠' 를 가져와야 합니다. [사실은 대상 헤더의 상태 값] 을 가져와야 합니다. 이 '자물쇠' 를 가져와야만 대상의 초기화 작업을 수행할 수 있습니다.여기서 하나 말하자면 하나의 클래스의 정적 변수가 부여되면 클래스의 초기화를 촉발할 수 있습니다~~오른쪽에서 볼 수 있듯이 클래스 초기화의 특성을 빌려 한 대상이 초기화되기 전에 다른 라인에 의해 사용되어 메모리의 가시적인 문제를 피할 수 있습니다~
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Java의 이중 체크(Double-Check) 상세 정보Effecitve Java라는 책의 48조에서 이중 검사 모델을 언급했고 이런 모델은 Java에서 통상적으로 적용되지 않는다고 지적했다.이 모드의 구조는 다음과 같습니다. 이 모드는 다음 코드를 개선합니다. 이 코드...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.