Singleton 패턴과 synchronized
원문index
1. 간단한 Draconian synchronization
2. 더블 체크
3. 추가적인 개선
3-1. Early Initialization
3-2. Initialization on Demand
3-3. Enum Singleton
간단한 Draconian synchronization ↑
public class DraconianSingleton { private static DraconianSingleton instance; public static synchronized DraconianSingleton getInstance(){ if( instance == null ) { instance = new DraconianSingleton(); } return instance; } // other methods }
Double-Check Locking ↑
약점을 개선하기 위해서, 첫 부분에서 객체를 만들어야 하는 지를 먼저 확인하고 객체를 만들어야 할때만 락을 얻어야 하도록 만들 수 있다. --- 1더 나아가서, 연산의 원자성을 유지하기 위해서 동기화 블록에 들어가자마자 같은 확인 작업을 수행할 수 있다. --- 2
public class DclSingleton { private static volatile DclSingleton instance; public static DclSingleton getInstance() { if( instance == null ) { // --- 1 synchronized (DclSingleton.class) { if (instance == null) { // --- 2 instance = new DclSingleton(); } } } return instance; } // private constructor and other methods... }
다른 대안 ↑
더블 체크 버전도 더 속도를 높일 수 있다. 최소 2개의 이슈가 있다.- 제대로 작동하기 위해서는 volatile 키워드가 필요하기 때문에, Java 1.4 와 더 낮은 버전들에서는 호환되지 않는다.
- 약간 조잡하기 때문에 코드 가독성이 떨어진다.
Early Initialization ↑
쓰레드 안전성을 얻는 가장 쉬운 방법은 인라인으로 코드를 만들 거나, 동일한 static 블록을 이용할 수 있다. static 필드와 블록들은 하나씩 차례대로 초기화 된다는 점을 활용한다.public class EarlyInitSingleton { private static final EarlyInitSingleton INSTANCE = new EarlyInitSingleton(); public static EarlyInitSingleton getInstance() { return INSTANCE; } // private constructor and other methods... }
Initialization on Demand (Lazy Initialization)↑
클래스 초기화는 그 메소드들이나 필드들 중 하나에 접근하는 첫 순간에 일어나기 때문에, 늦은 초기화(lazy initialization) 을 구현하기 위해서 중첩 스태틱 클래스를 이용할 수 있다.public class InitOnDemandSingleton { private static class InstanceHolder { private static final InitOnDemandSingleton INSTANCE = new InitOnDemandSingleton(); } public static InitOnDemandSingleton getInstance(){ return InstanceHolder.INSTANCE; } // private constructor and other methods... }
Enum Singleton ↑
singleton 을 작성할 때 가장 쉬운 방법은 class 대신에 enum 클래스를 사용하는 방법이다. 작성할 때에는 가장 간결하고 안전한 방법이다.public enum EnumSingleton { INSTANCE; // other methods... }