면접 의 단일 예 모드

6783 단어 디자인 모드
단일 모델 은 디자인 모델 에서 가장 이해 하기 쉽 고 손 으로 코드 를 쓰기 쉬 운 모델 이 라 고 할 수 있 습 니 다.하지만 구덩이 가 적지 않 아 면접 문제 로 도 자주 출제 된다.본 고 는 주로 몇 가지 단 례 서법 에 대한 정 리 를 하고 그 장단 점 을 분석한다.많은 것 이 흔히 말 하 는 문제 이지 만, 만약 당신 이 어떻게 스 레 드 안전 한 단일 예 를 만 드 는 지 모른다 면, 이중 검사 자물쇠 가 무엇 인지 모른다 면, 이 글 은 당신 에 게 도움 이 될 것 입 니 다.
게으름뱅이
하나의 사례 모델 을 실현 하 겠 다 는 질문 에 많은 사람들의 첫 반응 은 다음 과 같은 코드 를 쓰 는 것 이 었 다. 교과서 에서 도 이렇게 가 르 쳤 다.
public class Singleton {
        private static Singleton instance;

        private Singleton() {
        }

        public static Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }

이 코드 는 간단명료 하고 게 으 른 로드 모드 를 사 용 했 지만 치 명 적 인 문제 가 있 었 다.여러 개의 스 레 드 가 getInstance () 를 병렬 호출 할 때 여러 개의 인 스 턴 스 를 만 듭 니 다.다 중 스 레 드 에서 제대로 작 동 하지 못 한 다 는 얘 기다.
게으름뱅이 식, 스 레 드 안전
위의 문 제 를 해결 하기 위해 서 가장 쉬 운 방법 은 전체 getInstance () 방법 을 동기 화 (synchronized) 로 설정 하 는 것 입 니 다.
public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

비록 스 레 드 가 안전 하고 여러 사례 의 문 제 를 해결 하 였 으 나 효율 적 이지 않다.getInstance () 방법 은 언제든지 하나의 스 레 드 만 호출 할 수 있 기 때 문 입 니 다.그러나 동기 화 작업 은 첫 번 째 호출 때 만 필요 합 니 다. 즉, 첫 번 째 인 스 턴 스 대상 을 만 들 때 입 니 다.이중 검사 자 물 쇠 를 끌 어 냈 다.
이중 검사 자물쇠
이중 검사 잠 금 모드 (double checked locking pattern) 는 동기 블록 에 잠 금 을 추가 하 는 방법 입 니 다.프로그래머 는 두 번 의 검사 가 있 기 때문에 이중 검사 자물쇠 라 고 부른다.  instance == null 한 번 은 동기 블록 밖 에 있 고 한 번 은 동기 블록 안에 있다.왜 동기 블록 안에서 다시 한 번 검사 해 야 합 니까?동기 블록 밖의 if 에 여러 개의 스 레 드 가 함께 들 어 갈 수 있 기 때문에 동기 블록 에서 2 차 검 사 를 하지 않 으 면 여러 개의 인 스 턴 스 가 생 성 됩 니 다.
public static Singleton getSingleton() {    
            if (instance == null) {      //                          
                synchronized (Singleton.class) {            
                    if (instance == null) {             //                        
                            instance = new Singleton();            
                    }    
                }
            } 
            return instance ;}
            }
        }

이 코드 는 보기에 매우 완벽 해 보이 지만, 애석 하 게 도 그것 은 문제 가 있다.주로 instance = new Singleton() 이 말 은 원자 조작 이 아니 라 사실상 JVM 에서 이 말 은 다음 과 같은 세 가지 일 을 했다.
인 스 턴 스에 메모리 할당 Singleton 의 구조 함 수 를 호출 하여 구성원 변 수 를 초기 화 합 니 다 인 스 턴 스 대상 을 분 배 된 메모리 공간 으로 가리 키 기 (이 단 계 를 실행 하면 인 스 턴 스 는 null 이 아 닙 니 다) 그러나 JVM 의 인 스 턴 트 컴 파일 러 에 명령 재 정렬 최적화 가 존재 합 니 다. (명령 재 정렬 을 알 고 싶다 면 앞의 글 을 보십시오. 다 중 스 레 드 명령 재 정렬)즉 위의 두 번 째 단계 와 세 번 째 단계 의 순 서 는 보장 할 수 없고 최종 집행 순 서 는 1 - 2 - 3 일 수도 있 고 1 - 3 - 2 일 수도 있다.후자 라면 3 실행 이 끝나 고 2 가 실행 되 지 않 기 전에 스 레 드 2 에 의 해 선점 되 었 습 니 다. 이때 인 스 턴 스 는 이미 null 이 아 닙 니 다 (그러나 초기 화 되 지 않 았 습 니 다). 그래서 스 레 드 2 는 인 스 턴 스 로 직접 돌아 가서 사용 한 다음 에 순조롭게 오 류 를 보고 합 니 다.
인 스 턴 스 변 수 를 volatile 로 만 설명 하면 됩 니 다.
public class Singleton {
        private volatile static Singleton instance; //     volatile

        private Singleton() {
        }

        public static Singleton getSingleton() {
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

일부 사람들 은 volatile 을 사용 하 는 이 유 는 가시 성 때 문 이 라 고 생각 합 니 다. 즉, 스 레 드 가 로 컬 에 인 스 턴 스 복사 본 이 저장 되 지 않 고 매번 메 인 메모리 에서 읽 을 수 있 도록 보장 할 수 있 습 니 다.하지만 사실은 옳지 않다.volatile 을 사용 하 는 주요 원인 은 또 다른 특성 이다. 명령 재 정렬 최적화 금지.즉, volatile 변수의 할당 작업 뒤에 메모리 장벽 (생 성 된 어 셈 블 리 코드) 이 있 고 읽 기 동작 은 메모리 장벽 이전 으로 정렬 되 지 않 습 니 다.예 를 들 어 위의 예 에서 추출 작업 은 1 - 2 - 3 을 실행 한 후에 또는 1 - 3 - 2 를 실행 한 후에 1 - 3 을 실행 한 후에 값 을 얻 는 상황 이 존재 하지 않 습 니 다.'선행 발생 원칙' 의 측면 에서 이해 하면 하나의 volatile 변수 에 대한 쓰기 작업 은 모두 뒤에서 이 변수 에 대한 읽 기 작업 (여기 의 '뒤' 는 시간 적 인 선후 순서) 에서 먼저 발생 하 는 것 이다.
하지만 자바 5 이전 버 전에 서 volatile 의 쌍 검 자 물 쇠 를 사용 한 것 은 문제 가 있 음 을 특히 주의 하 세 요.그 이 유 는 자바 5 이전의 JMM (자바 메모리 모델) 에 결함 이 있 기 때 문 입 니 다. 변 수 를 volatile 로 설명 하 더 라 도 정렬 을 완전히 피 할 수 없습니다. 주로 volatile 변수 전후의 코드 에 정렬 문제 가 존재 합 니 다.이 volatile 차단 정렬 문 제 는 자바 5 에서 만 복구 할 수 있 기 때문에 그 후에 야 volatile 을 안심 하고 사용 할 수 있 습 니 다.
당신 이 이런 복잡 하고 문 제 를 포함 하 는 방식 을 좋아 하지 않 을 것 이 라 고 믿 습 니 다. 물론 우 리 는 스 레 드 안전 을 실현 하 는 단일 모델 을 더욱 잘 실현 하 는 방법 이 있 습 니 다.
굶 주 림 식 static final field
이 방법 은 매우 간단 합 니 다. 하나의 인 스 턴 스 가 static 과 final 변수 로 밝 혀 졌 기 때문에 처음으로 메모리 에 클래스 를 불 러 올 때 초기 화 되 기 때문에 인 스 턴 스 를 만 드 는 것 자체 가 스 레 드 가 안전 합 니 다.
public class Singleton{
    //        
    private static final Singleton instance = new Singleton();
    
    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}

이런 표기 법 이 완벽 하 다 면, 그렇게 많은 쌍 검 자물쇠 문 제 를 잔소리 할 필요 가 없다.단점 은 게 으 른 로 딩 모드 (lazy initialization) 가 아 닙 니 다. 클 라 이언 트 가 getInstance () 방법 을 호출 하지 않 더 라 도 로 딩 클래스 가 처음부터 초기 화 됩 니 다.굶 주 린 사람의 생 성 방식 은 일부 장면 에서 사용 할 수 없습니다. 예 를 들 어 Singleton 인 스 턴 스 의 생 성 은 매개 변수 나 프로필 에 의존 하 는 것 입 니 다. getInstance () 전에 특정한 방법 으로 파 라 메 터 를 설정 해 야 합 니 다. 그러면 이러한 단일 쓰기 방법 은 사용 할 수 없습니다.
정적 내부 클래스 static nested class
저 는 정적 내부 클래스 의 방법 을 사용 하 는 경향 이 있 습 니 다. 이런 방법 도 에서 추천 한 것 입 니 다.
public class Singleton {
        private static class SingletonHolder {
            private static final Singleton INSTANCE = new Singleton();
        }

        private Singleton() {
        }

        public static final Singleton getInstance() {
            return SingletonHolder.INSTANCE;
        }
    }

이러한 쓰기 방법 은 JVM 자체 체 제 를 사용 하여 스 레 드 안전 문 제 를 확보 합 니 다.Singleton Holder 는 개인 적 인 것 이기 때문에 getInstance () 를 제외 하고 접근 할 수 없 기 때문에 게으름뱅이 식 입 니 다.동시에 인 스 턴 스 를 읽 을 때 동기 화 되 지 않 고 성능 결함 이 없습니다.JK 버 전에 의존 하지 도 않 고
열거 하 다.
일일이 사례 를 쓰 는 것 은 정말 간단 하 다!이것 도 그것 의 가장 큰 장점 이다.아래 의 이 코드 는 실례 를 열거 하 는 일반적인 방법 을 설명 하 는 것 이다.
public enum EasySingleton{
INSTANCE;
}

Easy Singleton. INSTANCE 를 통 해 인 스 턴 스 를 방문 할 수 있 습 니 다. 이것 은 getInstance () 를 호출 하 는 방법 보다 훨씬 간단 합 니 다.매 거 진 기본 값 을 만 드 는 것 은 스 레 드 가 안전 하기 때문에 double checked locking 을 걱정 할 필요 가 없 으 며, 역 직렬 화 로 인해 새로운 대상 을 다시 만 드 는 것 도 방지 할 수 있 습 니 다.그래도 이렇게 쓰 는 사람 은 드 물 어 요. 익숙 하지 않 아서 그런 가 봐 요.
총결산
일반적으로 하나의 사례 모델 은 게으름뱅이, 굶 주 린 사람, 이중 검사 자물쇠, 정태 내부 류, 매 거 진 다섯 가지 방법 이 있다.상술 한 것 은 모두 라인 안전 의 실현 이 고 글 의 첫머리 에 제 시 된 첫 번 째 방법 은 정확 한 서법 이 아니다.
저 는 개인 적 으로 일반적인 상황 에서 굶 주 린 사람 식 을 직접 사용 하면 됩 니 다. 게 으 른 로 딩 (lazy initialization) 을 요구 하면 정적 내부 류 를 사용 하 는 경향 이 있 습 니 다. 반 직렬 화 생 성 대상 과 관련 될 때 매 거 진 방식 으로 단일 예 를 실현 하려 고 합 니 다.

좋은 웹페이지 즐겨찾기