Singletone
기존 문서
public final class Singleton{
private static volatile Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if (instance == null) {
synchronized(Singletone.class){
if (instance=null){
instance = new Singleton();
}
}
}
return instacne;
}
}
동기화가 없었다면, 멀티 스레드로 인해 싱글톤이 깨질 수 있다.
주의 :
DCLP
는 안티패턴 중 하나이기에 synchronized 를 통해 안전장치를 건다.
public final class Singleton{
private Singleton(){}
private static SingletonHolder(){
private static final SingleTon INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
public class Elvis{
public static final Elvis instance = new Elvis();
private Elvis(){}
}
클라이언트 코드
public SingletonTest{
public static void main(String[] args){
Elvis SingletonElvis = Elvis.instance;
}
생성자는 1번 호출되고 Elvis는 싱글톤이 된다. (유일한 인스턴스를 제공한다.)
-> 리플랙션을 통해 생성자는 여러번 호출될 수 있다. (카운트
, flag
등을 통해 싱글톤을 유지할 수 있다.)
reflection
을 통한 싱글톤 패턴 깨기public SingletonTest{
public static void main(String[] args){
Elvis SingletonElvis = Elvis.instance;
Elvis SingletonElvis2 = null;
try{
Constructor<Elvis> constructor = Elvis.class.getConstructor;
constructor.setAccessible(true);
SingletonElvis2 = (Elvis)constructor.newInstance();
}
}
public class Elvis{
private static final Elvis instance = new Elvis();
private Elvis(){}
public static Elvis getInstance(){
return INSTANCE;
}
}
장점
getInstance가 쓰레드가 호출될 때마다 새로운 객체가 만들어지도록 변경하더라도 클라이언트의 코드는 수정하지 않아도 된다.
Generic의 싱글톤 팩토리 생성 가능
Supplier<>에 대한 메소드 레퍼런스로 사용할 수 있다.
직렬화
위의 두가지 모두 직렬화에 사용한다면 역직렬화 할 때 같은 타입의 인스턴스가 여러 개 생길 수 있다. 그 문제를 해결하기 위해서는 모든 인스턴스 필드에 transient
를 추가하고, readResolve
메소드를 다음과 같이 구현하여야 한다.
private Object readResolve(){
return INSTANCE;
}
Enum
직렬화, 역직렬화, 리플렉션으로 인한 문제 등 거의 모든 문제가 해결된다. (이상적인 대안)
public enum Elvis{
INSTACNE;
}
Enum말고 다른 상위의 클래스를 상속해야만 한다면 사용할 수 없다.
스프링을 사용한다면, Bean을 등록하고 호출하면, 싱글톤이 보장된다.