몇 가지 흔히 볼 수 있 는 단일 모델
6567 단어 디자인 모델 총화
가장 원시 적
public class Singleton {
//
private static Singleton singleton;
//
private Singleton(){}
//
public static Singleton getInstance(){
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
단점 분석: 높 은 병발 을 고려 하지 않 는 상황 에서 이런 방식 은 문제 가 없 지만 높 은 병발 상황 에서 여러 개의 사례 가 발생 할 수 있다.
원인 분석: 높 은 동시 방문 시 스 레 드 A 가 getInstance 방법 에 들 어가 if 조건 판단 에 들 어 갑 니 다. 이때 singleton 은 비어 있 습 니 다. 그리고 인 스 턴 스 를 만 듭 니 다. 스 레 드 B 가 들 어 왔 을 때 인 스 턴 스 가 아직 만 들 어 졌 고 끝나 지 않 았 기 때문에 singleton 은 비어 있 습 니 다. 스 레 드 B 가 다시 들 어 왔 습 니 다. 인 스 턴 스 를 만 듭 니 다.이것 은 여러 개의 실례 를 만 들 었 다.
2. 첫 번 째 방식 개선
public class BadSynchronizedSingleton {
//
private static BadSynchronizedSingleton synchronizedSingleton;
//
private BadSynchronizedSingleton(){}
//
public synchronized static BadSynchronizedSingleton getInstance(){
if (synchronizedSingleton == null) {
synchronizedSingleton = new BadSynchronizedSingleton();
}
return synchronizedSingleton;
}
}
단점 분석: 여기 서 인 스 턴 스 를 만 드 는 방법 을 동기 화 합 니 다. 이렇게 한 스 레 드 가 이 방법 에 접근 할 때 다른 모든 스 레 드 는 걸 려 있 는 대기 상태 에 있어 야 합 니 다. 오히려 방금 동기 화 방문 이 여러 개의 인 스 턴 스 를 만 들 위험 을 피 할 수 있 습 니 다.이것 은 의심 할 여지없이 코드 의 집행 효율 에 영향 을 주 는 것 이다
3. 이중 잠 금
public class SynchronizedSingleton {
//
private static SynchronizedSingleton synchronizedSingleton;
//
private SynchronizedSingleton(){}
//
public static SynchronizedSingleton getInstance(){
if (synchronizedSingleton == null) {
synchronized (SynchronizedSingleton.class) {
if (synchronizedSingleton == null) {
synchronizedSingleton = new SynchronizedSingleton();
}
}
}
return synchronizedSingleton;
}
}
분석: 이런 방법 은 위의 가장 머리 가 없 는 동기 화 방법 에 비해 훨씬 좋 습 니 다. 왜냐하면 우 리 는 현재 인 스 턴 스 가 null, 즉 인 스 턴 스 가 생 성 되 지 않 았 을 때 동기 화 를 하 는 것 입 니 다. 그렇지 않 으 면 바로 돌아 갑 니 다. 그러면 불필요 한 스 레 드 대기 시간 을 많이 절약 할 수 있 습 니 다. 주의해 야 할 것 은 동기 화 블록 에서 우 리 는 synchronized Singleton 이 null 인지 아 닌 지 를 다시 판단 하 였 습 니 다.왜 그 랬 는 지 설명해 봐.만약 에 우리 가 동기 블록 중의 null 인지 아 닌 지 를 판단 한다 면 이런 상황 이 있 습 니 다. A 스 레 드 와 B 스 레 드 가 모두 동기 블록 밖에서 synchronized Singleton 을 null 로 판단 했다 고 가정 하면 A 스 레 드 는 먼저 스 레 드 자 물 쇠 를 얻 었 고 동기 블록 에 들 어간 다음 에 A 스 레 드 는 인 스 턴 스 를 만 들 것 입 니 다. 이때 synchronized Singleton 은 인 스 턴 스 를 부 여 받 았 고 A 스 레 드 는 동기 블록 을 종료 합 니 다.첫 번 째 로 만 든 인 스 턴 스 를 직접 되 돌려 주 었 습 니 다. 이때 B 스 레 드 는 스 레 드 자 물 쇠 를 얻 었 고 동기 블록 에 들 어 갔 습 니 다. 이때 A 스 레 드 는 인 스 턴 스 를 만 들 었 습 니 다. B 스 레 드 의 정상 적 인 상황 은 바로 돌아 가 야 합 니 다. 그러나 동기 블록 에서 null 인지 아 닌 지 를 판단 하지 않 고 인 스 턴 스 를 만 드 는 문 구 였 기 때문에 B 스 레 드 도 인 스 턴 스 를 만들어 서 되 돌려이때 여러 개의 실례 를 창조 한 상황 을 초래 했다.
단점 분석: 가상 컴퓨터 가 인 스 턴 스 를 만 드 는 이 작업 을 수행 할 때 사실은 여러 단계 로 나 누 어 진행 되 었 기 때 문 입 니 다. 즉, 새로운 대상 을 만 드 는 것 은 원자 작업 이 아 닙 니 다.일부 JVM 에서 상술 한 방법 은 문제 가 없 지만, 어떤 경우 에는 알 수 없 는 오 류 를 초래 할 수 있다.먼저 JVM 에서 새로운 대상 을 만 들 때 주로 세 단 계 를 거 쳐 야 한 다 는 것 을 알 아야 합 니 다. * *1. 메모리 할당 2. 구조 기 초기 화 3. 대상 이 분 배 된 메모리 의 주소 * * 를 가리 키 는 순 서 는 상기 이중 잠 금 방식 에 문제 가 없습니다. 이 경우 JVM 은 전체 대상 의 구 조 를 완성 한 것 이기 때문에 메모리 의 주 소 를 대상 에 게 건 네 주 었 습 니 다.그러나 2 와 3 단계 가 상반 되 는 경우 (2 와 3 이 상반 되 는 것 은 JVM 이 바이트 코드 를 조정 하기 때 문 일 수 있 으 며, 그 중 하 나 는 명령 의 집행 순 서 를 조정 하 는 것) 문제 가 발생 할 수 있다.이 때 메모리 주 소 를 대상 에 게 부여 하기 때문에 상기 이중 잠 금 을 추가 합 니 다. 즉, 분 배 된 메모리 주 소 를 synchronized Singleton 에 가리 키 고 초기 화 구조 기 를 진행 합 니 다. 이 때 뒤의 스 레 드 가 getInstance 방법 을 요청 할 때 synchronized Singleton 대상 이 이미 예화 되 었 다 고 생각 하고 인용 을 되 돌려 줍 니 다.구조 기 를 초기 화하 기 전에 이 스 레 드 가 synchronized Singleton 을 사용 하면 알 수 없 는 오류 가 발생 할 수 있 습 니 다.
최종
public class Singleton {
private Singleton(){}
public static Singleton getInstance(){
return SingletonInstance.instance;
}
private static class SingletonInstance{
static Singleton instance = new Singleton();
}
}
분석: 먼저 이런 방식 이 왜 위 에서 알 수 없 는 오 류 를 피 할 수 있 는 지 알 아 보 겠 습 니 다. 주로 하나의 정적 속성 이 첫 번 째 로 클래스 를 불 러 올 때 만 초기 화 되 기 때 문 입 니 다. 이것 은 JVM 이 우리 에 게 보증 해 준 것 이기 때문에 우 리 는 동시 방문 문 제 를 걱정 할 필요 가 없습니다.따라서 초기 화 를 반 쯤 진행 할 때 다른 스 레 드 는 사용 할 수 없습니다. JVM 이 이 과정 을 강제로 동기 화 하 는 데 도움 을 줄 수 있 기 때 문 입 니 다.또한 정적 변 수 는 한 번 만 초기 화 되 기 때문에 singleton 은 여전히 하나의 예 입 니 다.