객체의 인스턴스가 오직 1개만 생성되어, memory 상에 1개만 존재하는 패턴을 의미한다. 이렇게 만들어진 인스턴트는 전역적으로 접근이 가능하다. 따라서, concurrency에 주의해야 한다. (singleton 에 접근하고 있는 쓰레드가 여러개면?)
Bean 은 스프링이 IoC 방식으로 관리하여, 스프링이 직접 생성과 제어를 담당하는 객체이다. 이 때, 스프링은 기본적으로 빈 객체를 모두 싱글톤으로 만든다. (spring bean 의 scope 이 singleton)
싱글톤 구현 법으로 발생하는 다양한 문제점을, 스프링은 싱글톤 컨테이너를 제공해서 해결한다. 빈 생성에 대한 제어권이 컨테이너에게 있기 때문에, 싱글톤 객체 생성을 위해 private 생성자를 꼭 사용하지 않아도 된다. 컨테이너가 빈을 싱글톤으로 생성하여, public 생성자를 가진 java 클래스를 싱글톤으로 활용할 수 있다.
가장 간단한 방법이다.
단, 기동 시간에 문제가 발생한다. 클래스 로딩 단계에서 모든 싱글톤 클래스의 인스턴스를 생성하기 때문에, 해당 인스턴스를 사용하지 않더라도 모든 싱글톤이 준비가 되어야 한다. 따라서, demand 될 때 init 하는 것이 필요하다.
public class Singleton {
private static final Singleton instance = new Singleton();
private SingleTone(){}
public static Singleton getInstance(){
return instance;
}
}
인스턴스 낭비 문제는 해결된다.
하지만, multi-thread 환경에서 동기화 문제가 발생한다. 인스턴스가 생성되지 않은 시점에서 여러 쓰레드가 동시에 getInstance() 함수를 호출하면, 여러 싱글톤이 생성될 수 있다.
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
synchronized 키워드를 통해, 오직 하나의 쓰레드만 접근 가능하게 한다.
하지만, synchronized 키워드 자체에 대한 비용이 커(lock 을 걸어 blocking 되기 때문에) 싱글톤 인스턴스 호출이 잦을 때에는 성능이 떨어진다.
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static synchronized Singledton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
대부분의 언어에서 사용하는 방법.
메소드가 아닌 null 일 경우에만 synchronized.
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singledton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
클래스 안에 클래스(Holder) 를 두어 JVM 에 의존한 방식. Java 의 class loader 가 싱글 쓰레드이기 때문에 thread safe 를 보장할 수 있다.
중첩클래스 SingletonHolder 는 getInstance 메소드가 호출되기 전에는 생성되지 않으며(lazy initialization), final 키워드를 사용해서 다시 값이 할당되지 않도록 한다.
public class Singleton {
private Singleton(){}
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
지금까지의 모든 방법은, Java reflection 을 통해 싱글톤이 둘 이상의 인스턴스가 생성되게 할 수 있다.
enum type 은 프로그램 내에서 한번만 초기화되기 때문에, 이를 이용해 싱글톤을 구현한 방법이다. 하지만 역시, eager initialization 문제가 있으며 유연성이 떨어진다는 한계가 있다.
public enum SingletonEnum {
INSTANCE;
public static void execute() {
// ...
}
}