[객체 생성 패턴] Chapter 1-2. Singleton Pattern : 멀티 쓰레드에서도 안전하게

 

✍️ 멀티 쓰레드에서 안전하지 않은 Singleton

Thread A가 if의 조건을 통과하고 new를 하기 전에 Thread B가 if문을 검사한다면 Thread A와 B 모두 인스턴스를 생성하게 된다.

 

🍊 멀티 쓰레드에서 안전한 싱글톤 패턴

1, synchronized

synchronized 키워드를 사용해서 메서드를 동기화하는 방법으로, 한 번에 하나의 쓰레드만 메서드를 통과하게 된다.

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

간단하고 쉬운 방법이지만 단점으로 동기화 처리로 인해 성능의 이슈가 생길 수 있다. 동기화 메커니즘은 lock을 잡고 푸는 일련의 행위로 비용이 발생하므로 적절한 trade-off를 계산해서 사용하는 것이 바람직하다.

 

2, 이른 초기화 (eager initialization)

만약에 객체를 나중에 만들지 않아도 되며 객체를 생성하는 비용이 비싸지 않다면 미리 만들 수 있다.

싱글톤 인스턴스는 클래스가 로딩되는 시점에 초기화되므로 멀티 쓰레드에서도 안전하다. 다만 단점은 인스턴스를 미리 만든다는 것 자체가 단점이 될 수 있다. 인스턴스를 만드는 과정이 오래 걸리고 자원을 많이 소모하고 극단적으로 사용되지 않는다면 불필요한 자원 소모가 될 수 있다.

public class Settings {
    private static final Settings INSTANCE = new Settings();

    private Settings() {
    }

    public static Settings getInstance() {
        return INSTANCE;
    }
}

 

3, static inner 클래스 사용하기 

멀티 쓰레드 환경에서 안전하고 getInstance가 호출될 때 SettingsHolder class가 로딩되므로 객체를 lazy하게 생성하는 장점을 가지고 있다.

public class Settings {
    private Settings() {
    }

    private static class SettingsHolder {
        private static final Settings INSTANCE = new Settings();
    }

    public static Settings getInstance() {
        return SettingsHolder.INSTANCE;
    }
}

 

⚠️ 완전히 안전한 싱글톤 생성 방법일까?

static inner 클래스는 권장하는 싱글톤 생성 방법 중 하나이다. 하지만 지금까지 살펴봤던 모든 방법을 깨트리는 다양한 코딩 방법들이 존재한다.