JAVA

Singleton 구현 기법

devchive 2023. 8. 27. 20:18

Singleton 패턴

생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴한다.

 

기본적인 Singleton 패턴

public class Singleton {
    private static Singleton instance;

    private Singleton(){}

    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

Singleton의 getInstance를 호출할때 instance가 없다면 만들어주고 있다면 만들어둔 instance를 return 해준다. 이 코드는 스레드가 여러개일때 문제가 생길 수 있다. Tread A 와 Thread B가 동시에 instance == null 을 최초 검사한다면 두 스레드에서 instance를 각각 생성하여 instance가 두개가 될 수 있다.

 

synchronized를 이용한 패턴

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

위의 기본적인 패턴의 문제점을 해결하기 위해 synchronized 키워드를 넣었다. 이 경우 instance 생성은 스레드가 여러개이더라도 한번만 생성된다. 하지만 getInstance를 할때마다 불필요한 lock이 걸려 성능상 문제가 있을 수 있다.

 

DCL (Double Checked Locking) 패턴

public class Singleton {

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

위 synchroinzed 패턴의 성능을 향상시키기 위해 생성해주는 부분만 synchronized block 으로 실행시켜준다.

 

static 초기화를 이용한 패턴

public class Singleton {

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

최초 instance 생성을 getInstance 호출 시점이 아닌 클래스 가 로딩되는 시점에 생성해주는 방식이다. 간결하고 Thread-safe하다. 하지만 사용할지 안할지 모르는 instance를 미리 만들어두는것이 옳은것인가에 대한 문제가 있다. 

 

LazyHolder 패턴

public class Singleton {
	private Singleton(){}

	public static Singleton getInstance() {
		return LazyHolder.INSTANCE;
	}

	private static class LazyHolder {
		private static final Singleton INSTANCE = new Singleton();
	}
}

이 방법은 static 영역에서 초기화를 하지만 실제 getInstance로 LazyHolder를 참조할때 class가 로딩되며 초기화가 진행된다. 

 

참조

https://javaplant.tistory.com/21