GOF 23 학습 노트 (2) 왜 단일 모드 에서 이중 검사 자물쇠 에 결함 이 있 습 니까?

2976 단어 디자인 모드
이중 검출 잠 금 실현
public static Singleton3 getInstance() {  
    if (instance == null) {  
        synchronized (Singleton.class) {  
            if (instance == null) {  
                instance = new Singleton();  
            }  
        }  
    }  
    return instance;  
} 

이 방법 은 동기 화 된 내용 을 if 내부 로 이동 시 켜 처음 만들어 야 동기 화 되 고 효율 을 높 일 수 있 습 니 다.그러나 이 방법 은 명령 의 정렬 에 영향 을 받는다.
지령 재 정렬: 지령 재 정렬 은 지령 을 최적화 하고 프로그램의 운행 효율 을 높이 기 위 한 것 이다.명령 재 정렬 은 컴 파일 러 의 재 정렬 과 실행 시 재 정렬 을 포함한다.JVM 규범 에 따 르 면 명령 재 정렬 은 단일 스 레 드 프로그램의 실행 결과 에 영향 을 주지 않 는 전제 에서 진행 할 수 있다.예컨대 instance = new Singleton () 은 다음 과 같은 위조 코드 로 분해 할 수 있 습 니 다.
memory = allocate();   //1:           
ctorInstance(memory);  //2:       
instance = memory;     //3:  instance            

근 데 지령 을 받 고 정렬 을 다시 하면 이렇게 돼 요.
memory = allocate();   //1:           
instance = memory;     //3:  instance            
                       //  ,           !  
ctorInstance(memory);  //2:       

이렇게 하면 문제 가 발생 할 수 있 습 니 다. 스 레 드 A 가 intance = memory () 를 실 행 했 습 니 다. 이 단 계 는 스 레 드 B 를 볼 수 있 습 니 다. 그러면 스 레 드 B 가 if (intance = = null) 를 판단 할 때 intance 가 비어 있 지 않 은 것 을 발견 하고 intance 로 돌아 갑 니 다. 그러나 intance 는 내 장 된 주 소 를 가리 키 기 때문에 실제 초기 화 되 지 않 았 습 니 다.그러면 스 레 드 B 는 완전히 초기 화 되 지 않 은 인 스 턴 스 를 되 돌려 줍 니 다.
이중 검출 잠 금 수정판
따라서 수정판 에 서 는 두 개의 동기 블록 에 국부 변 수 를 추가 하여 이 문 제 를 해결 하려 고 합 니 다. 먼저 동기 블록 으로 국부 변 수 를 초기 화하 고 완전히 초기 화 한 후에 인 스 턴 스에 값 을 부여 하여 인 스 턴 스 가 완전히 초기 화 되 었 음 을 보증 합 니 다.
public class Singleton3 {
	private static Singleton3 instance = null;
	
	private Singleton3() {};
	
	public static Singleton3 getInstance() {
		if(instance == null) {
			Singleton3 sc;
			synchronized (Singleton3.class) {
				sc = instance;
				if(sc == null) {
					synchronized (Singleton3.class) {
						if(sc == null) {
							sc = new Singleton3();
						}
					}
				instance = sc;
				}
			}
		}
		return instance;
	}
}
그러나 이런 방법 은 여전히 문제 가 존재 합 니 다. 자바 는 동기 블록 안의 내용 을 대상 잠 금 이 풀 리 기 전에 실행 해 야 한다 고 규정 하고 있 습 니 다. (즉, 하나의 스 레 드 가 실행 되 어야 다른 스 레 드 가 실 행 될 수 있 습 니 다) 그러나 동기 블록 이외 의 코드 는 규정 되 어 있 지 않 습 니 다. 동기 블록 이 실 행 된 후에 실행 해 야 한 다 는 것 을 의미 합 니 다.
instance = sc 운행 과정 에서 내부 동기 블록 으로 달 려 갈 가능성 이 높다.이렇게 해서 명령 을 다시 정렬 하 는 문제 가 다시 발생 했 습 니 다. 즉, sc 가 메모리 주 소 를 가리 키 고 초기 화 되 지 않 았 을 때 인 스 턴 스 에 값 을 부여 하여 외부 스 레 드 가 완전히 초기 화 되 지 않 은 인 스 턴 스 를 직접 얻 었 습 니 다.
JDK 1.5 이후, volatile 변 수 를 사용 하여 DCL 을 유효 하 게 정렬 하 는 명령 을 금지 할 수 있 습 니 다.
public class Singleton3 {  
    //  volatile     
    private static volatile Singleton3 instance;  
  
    private Singleton3() {  
    }  
  
    public static Singleton3 getInstance() {  
        if (instance == null) {  
            synchronized (Singleton3.class) {  
                if (instance == null) {  
                    instance = new Singleton3();  
                }  
            }  
        }  
        return instance;  
    }  
}

좋은 웹페이지 즐겨찾기