Skip to content

싱글톤 패턴은 애플리케이션이 시작될 때, 어떤 클래스가 최초 한 번만 메모리를 할당(static)하고 인스턴스를 생성하여 사용하는 패턴이다. 똑같은 인스턴스를 여러개 생성하지 않고, 어디에서 접근하든 기존에 만들었던 인스턴스를 이용하도록 한다.


예시 코드

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

가장 간단하게 싱글톤 패턴을 구현한 코드이다. getInstance() 메서드를 호출 할 떄 인스턴스를 생성하기 때문에 Lazy하다는 특징이 있다. 하지만 멀티 쓰레드 환경에서 동시 접속이 발생하면 인스턴스가 여러개 생성될 수 있다는 단점이 있다.


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

멀티 쓰레드 문제를 해결하기 위해 위처럼 클래스가 메모리에 할당되는 시점에 인스턴스를 생성하는 방법을 사용할 수도 있다. 하지만 이 방식을 사용하면 이 클래스가 사용되지 않더라도 인스턴스를 생성해야 하기 때문에 메모리가 낭비된다.


public class InnerHolderSingleton {
private InnerHolderSingleton() { }
private static class SingletonHolder {
private static final InnerHolderSingleton INSTANCE = new InnerHolderSingleton();
}
public static InnerHolderSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

위처럼 innerHolder를 사용해서 싱글톤을 구현하는 방법도 있다. 이렇게 하면 멀티 쓰레드문제를 해결하면서 Lazy한 특성을 가질 수 있다. 중첩클래스 SingletonHolder는 getInstance() 메소드가 호출되기 전에는 참조 되지 않으며,최초로 getInstance() 메소드가 호출될 때 클래스로더에 의해 Singleton 인스턴스를 생성하여 리턴하기 때문에 Lazy하다. 그리고 SingletonHolder 내부 인스턴스는 static 이기 때문에 클래스 로딩 시점에 한 번만 호출되고, final을 사용해 다시 값이 할당되지 않는다.

하지만 이렇게 열심히 구현해도 리플렉션을 쓰면 생성자에 접근하여 싱글톤을 꺠뜨릴 수 있다. 특히 역직렬화할때 이 리플렉션으로 인해 문제가 생길 수 있기 때문에 아래와 같이 리플렉션에 안전한 Enum을 사용해서 싱글톤을 구현하기도 한다.


public enum EnumSingleton {
INSTANCE;
}

싱글톤 패턴은 프로그램의 성능과 동시성 문제를 고려해야 하기 떄문에 다양한 방식으로 구현될 수 있다.

링크로 가면 코드를 볼 수 있다.

출처:
https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4