이펙티브 자바3 : 싱글톤

참치돌고래·2021년 8월 30일
0

이펙티브 자바

목록 보기
3/21

private 생성자 또는 enum 타입

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;
    }
        
}

1.final field


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();
                
            
        }
    }

2.static 팩토리 메소드


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을 등록하고 호출하면, 싱글톤이 보장된다.

profile
안녕하세요

0개의 댓글