SingletonHoler 패턴

일반적인 싱글톤 패턴은 멀티스레드 환경에서 단일 인스턴스 생성을 보장하지 못한다. 그래서 멀티스레드에서도 사용가능한 방법이다.

멀티스레드에서의 싱글톤의 안정성에 대한 방법들

방법0 - 일반적인 싱글톤

스레드 안전하지 않음

public class Singleton {
    private final static Singleton INSTANCE = null;
  
    private Singleton() {
        // ...
    }
 
    private static Singleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton();
        }
  
        return INSTANCE;
    }
}

방법1 - static 선언

스레드 문제는 해결해주지만, 이 방법의 단점은 사용할 시점에 생성하는 것(lazy loading,lazy construct─늦은 로딩/늦은 생성)이 아니라 Singleton 이라는 클래스가 로딩될 그 시점에 생성된다는 것이다.

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

방법2 - syncronized 추가

스레드 문제는 해결해주지만, 위 방법은 getInstance 메서드 전체를 동기화 잠금을 걸기 때문에 성능이 좋지 않다.

public class Singleton {
    private static Singleton INSTANCE = null;
 
    private Singleton() {
        // ...
    }
 
    // synchronized 키워드 추가
    private static synchronized Singleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton();
        }
 
        return INSTANCE;
    }
}

방법3

방법2과 다를 바 없다(...) syncronized 를 안에다 하나 밖에다 하나 .. 뭐 거기서 거기

public class Singleton {
    private static Singleton INSTANCE = null;
 
    private Singleton() {
        // ...
    }
 
    // synchronized 키워드를 안쪽에 추가
    private static Singleton getInstance() {
        synchronized (Singleton.class) {
            if(INSTANCE == null) {
                INSTANCE = new Singleton();
            }
        }
 
        return INSTANCE;
    }
}

그래서 보완된 SingletonHoler 패턴

아래의 방법은 static inner class를 이용하여 동기화 문제를 피하고, 필요한 시점에 인스턴스를 생성한다. 원리는 static의 로딩시점을 이용한 것이다. 모든 자바 버전에서도 잘 돌아간다.

public class Singleton {
    private final static class SingletonHoler {
        public final static Singleton INSTANCE = new Singleton();
    }
 
    private Singleton() {
        System.out.println("싱글톤 생성!");
    }
 
    private static Singleton getInstance() {
        return SingletonHoler.INSTANCE;
    }
}