디자인 모델 을 깊이 학습 하 다.

6926 단어
더 읽 기
디자인 모델 에서 단일 모델 (singleton) 은 응용 이 가장 보편적 인 디자인 모델 이 라 고 할 수 있다.말 그대로 하나의 예 는 대상 의 유일한 인 스 턴 스 를 가 져 오 는 것 입 니 다. 대상 생 성 모델 로 대상 의 구체 적 인 인 인 스 턴 스 를 만 드 는 데 사 용 됩 니 다. 시스템 의 한 가지 인 스 턴 스 만 생 성 할 수 있 습 니 다.자바 언어 에서 이런 행 위 는 두 가지 장점 을 가 져 올 수 있다. 1. 자주 사용 하 는 대상 에 게 대상 을 만 드 는 데 걸 리 는 시간 과 소 모 를 생략 할 수 있다. 이것 은 중량급 대상 에 게 매우 상당 한 비용 이다.2. new 작업 의 횟수 가 줄 어 들 기 때문에 시스템 메모리 의 사용 빈도 도 낮 아 지고 GC 의 압력 을 줄 이 며 GC 의 정지 시간 을 단축 시 킬 수 있 습 니 다.따라서 시스템 의 핵심 구성 요소 와 자주 사용 되 는 대상, 예 를 들 어 시스템 에서 자주 사용 되 는 사전 값 저장 등 단일 모드 를 사용 하면 시스템 의 성능 을 효과적으로 개선 할 수 있다.
1. 간단 한 실현:

public Class Singleton{
    private Singleton(){
    }
    private static Singleton instance = new Singleton();
    private static Singleton getInstance(){
        return instance;
    }
}

예 를 들 어 외부 사용자 가 SingletonClass 의 인 스 턴 스 를 사용 해 야 한다 면 getInstance () 방법 만 사용 할 수 있 고 그 구조 방법 은 private 입 니 다. 그러면 한 대상 만 존재 할 수 있 습 니 다.
2. 로드 지연:
상기 사례 의 실현 은 매우 간단 하고 신뢰 할 수 있 습 니 다. 유일한 부족 한 것 은 클래스 에 대해 로드 지연 을 할 수 없다 는 것 입 니 다. 예 를 들 어 하나의 사례 의 생 성 과정 이 느 리 고 intance 구성원 변 수 는 static 입 니 다. JVM 에서 하나의 사례 류 를 불 러 올 때 생 성 됩 니 다. 만약 에 이때 이 사례 류 는 시스템 에서 다른 역할 을 하고 있 습 니 다.그러면 이 단일 클래스 의 방법 을 사용 하 는 모든 곳 에서 이 단일 변 수 를 초기 화 합 니 다. 사용 되 든 안 되 든 간 에.그럼 어 떡 하지?아래 의 방안 을 볼 수 있다.

public Class LazySingleton{
    private LazySingleton(){
    }
    private static LazySingleton instance = null;
    private static synchronized LazySingleton getInstance(){
        if(instance == null ){
            instance = new LazySingleton();
        }
        return instance;
    }
}

우선 정적 구성원 변수 인 스 턴 스 의 초기 값 에 null 을 부여 하여 시스템 이 시 작 될 때 추가 부하 가 없 도록 합 니 다.그 다음 에 getInstance () 방법 을 호출 할 때 현재 단일 사례 가 존재 하 는 지 여 부 를 먼저 판단 합 니 다. 존재 하지 않 으 면 단일 사례 를 만 들 고 중복 생 성 을 피 합 니 다. 그러나 다 중 스 레 드 에 서 는 synchronized 키워드 로 수식 해 야 합 니 다. 스 레 드 A 가 생 성 과정 에 들 어가 기 전에 스 레 드 B 도 비 어 있 는 판단 을 통 해 중복 생 성 되 지 않도록 해 야 합 니 다.
3. 동기 화 성능
상기 코드 는 하나의 예 를 완벽 하 게 실현 한 것 처럼 보 입 니 다. 동기 화 자물쇠 가 있 습 니 다. 하나의 스 레 드 는 다른 스 레 드 가 만들어 진 후에 야 이 방법 을 사용 할 수 있 습 니 다. 이것 은 하나의 사례 의 유일 성 을 보장 합 니 다.하지만 synchronized 에 의 해 수 정 된 동기 블록 은 일반 코드 보다 훨씬 느 립 니 다. getInstance () 호출 이 여러 번 존재 한다 면 성능 문 제 는 고려 할 수 밖 에 없습니다!그렇다면 로드 지연 을 도입 하기 위해 동기 화 키 워드 를 사용 하 는 것 은 오히려 시스템 성능 을 떨 어 뜨 렸 다. 얻 는 것 보다 잃 는 것 이 많 지 않 은 가?도대체 모든 방법 에 자 물 쇠 를 넣 어야 하 는 지, 아니면 그 중의 한 마디 에 자 물 쇠 를 넣 으 면 충분 한 지 분석 해 보 자.우 리 는 왜 자 물 쇠 를 넣 어야 합 니까?로드 지연 상황 이 발생 한 원인 을 분석 해 보 세 요.그 이 유 는 null 의 조작 과 생 성 대상 의 조작 이 분리 되 었 기 때 문 입 니 다.만약 이 두 조작 이 원자 적 으로 진 행 될 수 있다 면, 단 례 는 이미 보장 되 었 을 것 이다.그래서 우 리 는 코드 를 수정 하기 시작 했다.

public Class LazySingleton{
    private LazySingleton(){
    }
    private static LazySingleton instance = null;
    private static LazySingleton getInstance(){
        synchronized (LazySingleton.class) { 
            if(instance == null ){
                instance = new LazySingleton();
            }
        }
        return instance;
    }
}

먼저 getInstance () 의 동기 화 동작 을 제거 한 다음 동기 화 자 물 쇠 를 if 문장 에 불 러 옵 니 다.그러나 이러한 수정 은 아무런 역할 을 하지 않 습 니 다. getInstance () 를 호출 할 때마다 동기 화 되 어야 하기 때문에 성능 문제 가 존재 합 니 다.만약 에...

public Class LazySingleton{
    private LazySingleton(){
    }
    private static LazySingleton instance = null;
    private static LazySingleton getInstance(){
        if(instance == null){
            synchronized (LazySingleton.class) { //  1
                if(instance == null ){    //  2
                    instance = new LazySingleton();//  3
                }
            }
        }
        return instance;
    }
}

또 질문 있어 요?먼저 인 스 턴 스 가 null 인지 아 닌 지 판단 하고 null 이면 잠 금 을 추가 하여 초기 화 합 니 다.null 이 아니라면 인 스 턴 스 로 돌아 갑 니 다.지금까지 모든 것 이 완벽 했다.우 리 는 아주 교묘 한 방식 으로 단일 모델 을 실현 했다.
4、Double-Checked Locking
이 건 사실 DoubleChecked Locking 디자인 이 실현 한 단일 모델 은 지금까지 문제, 성능 과 공존 을 완벽 하 게 해결 한 것 처럼 보이 지만 정말 그런 가?분석 해 보 자. 문 구 를 직접 보 자. 3. JIT 가 만 든 어 셈 블 리 코드 가 하 는 일 은 Lazy Singleton 대상 을 직접 만 든 다음 에 주 소 를 intance 에 부여 하 는 것 이 아니 라 반대로...실행 순 서 는 다음 과 같 습 니 다. 1. 메모리 2 를 신청 합 니 다. 이 메모리 주 소 를 intance 3 에 부여 합 니 다. intance 가 가리 키 는 주소 에 대상 을 구축 해 보 세 요. 만약 에 스 레 드 스케줄 이 intance 에 메모리 주 소 를 부여 했다 면 Singleton 의 구조 함수 가 호출 되 지 않 은 미묘 한 시간 입 니 다.그러면 이 함수 에 들 어간 다른 스 레 드 는 인 스 턴 스 가 null 이 아니 라 는 것 을 알 게 되 어 안심 하고 대담 하 게 인 스 턴 스 를 되 돌려 사용 할 수 있 습 니 다.그런데 이 불쌍 한 라인 은 모 르 겠 어 요. 이때 인 스 턴 스 가 초기 화 되 지 않 았 어 요!자바 의 memory model 은 out - of - order write 를 허용 하기 때문에 현재 의 문제점 은 다음 과 같다. 우선, 하나의 대상 을 구성 하 는 것 은 원자 조작 이 아니 라 끊 길 수 있 는 것 이다.둘째, 더 중요 한 것 은 자바 가 초기 화 되 기 전에 대상 의 주 소 를 다시 쓸 수 있 도록 허용 하 는 것 이 바로 out - of - order 입 니 다.
5. 실현 방안
이렇게 많은 말 을 했 는데 어째서 완벽 한 실현 방안 이 없 습 니까?JDK 5 이후 자바 는 새로운 메모리 모델 을 사용 했다.volatile 키 워드 는 명확 한 의 미 를 가진다. JDK 1.5 이전에 volatile 은 키워드 이지 만 그 용 도 를 명확 하 게 규정 하지 않 았 다. volatile 에 의 해 수 정 된 쓰기 변 수 는 이전의 읽 기와 쓰기 코드 와 조정 할 수 없고 읽 기 변 수 는 뒤의 읽 기와 쓰기 코드 와 조정 할 수 없다!따라서 인 스 턴 스 를 간단하게 volatile 키 워드 를 추가 하면 됩 니 다.

public Class LazySingleton{
    private LazySingleton(){
    }
    private volatile static LazySingleton instance = null;
    private static LazySingleton getInstance(){
        if(instance == null){
            synchronized (LazySingleton.class) { //  1
                if(instance == null ){    //  2
                    instance = new LazySingleton();//  3
                }
            }
        }
        return instance;
    }
}

volatile 키 워드 는 메모리 의 가시 성 을 확보 합 니 다. 최신 값 은 모든 라인 에서 즉시 볼 수 있 습 니 다. 사실은 또 다른 실현 방안 이 있 습 니 다. 바로 내부 클래스 를 사용 하 는 것 입 니 다.

public class Singleton{
    private static class SingletonHolder{
        private static final Singleton instance = new Singleton();
    }
    public static SingletonHolder getInstance(){
        return SingletonHolder.instance;
    }
 
    private Singleton(){       
    }
}

이 구현 에서 단일 모드 는 내부 클래스 를 사용 하여 단일 사례 의 인 스 턴 스 를 유지 합 니 다. Singleton 이 불 러 올 때 내부 클래스 는 초기 화 되 지 않 습 니 다. Singleton 이 가상 컴퓨터 에 불 러 올 때 단일 클래스 를 초기 화 하지 않 고 getInstance () 방법 이 호출 될 때 Singleton 을 불 러 와 intance 를 초기 화 할 수 있 습 니 다. 또한 JSL 규범 은 정의 합 니 다.클래스 의 구 조 는 반드시 원자 적 이 고 동시 다발 적 이지 않 아야 하기 때문에 동기 블록 을 추가 할 필요 가 없다.마찬가지 로 이 구 조 는 동시 다발 적 이기 때문에 getInstance () 도 동기 화 할 필요 가 없다.일반적인 상황 에서 5 안의 두 가지 방식 은 시스템 에 유일한 사례 만 존재 하 는 것 을 확보 할 수 있 지만 예외 적 인 상황 도 있 기 때문에 시스템 에 여러 개의 사례 가 존재 할 수 있다. 예 를 들 어 반사 체 제 를 통 해 사례 류 안의 사유 구조 함 수 를 호출 하면 여러 개의 사례 가 생 길 수 있 지만 이런 상황 의 특수성 은 여기 서 토론 하지 않 는 다.그렇지 않 으 면 완벽 한 단일 모드 를 실현 하면 정말 철저하게 덮 을 것 이다!

좋은 웹페이지 즐겨찾기