Java/Java 자료실

[디자인 패턴] 05. 싱글턴 패턴 (Singleton Pattern)

Chipmunks 2018. 10. 23.
728x90


싱글턴 패턴 (Singleton Pattern)

싱글턴 패턴은 해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록 하기 위한 패턴입니다.


멀티스레딩 문제 해결 방법

여러 스레드에서 동시에 getInstance() 메소드로 싱글턴 객체를 요청할 때, new 생성이 중복될 가능성이 있다. 따라서 동기화를 해줘야 한다.


1. 메소드의 synchronized 키워드를 붙인다.

1
2
3
4
5
6
7
8
9
10
11
public class Singleton {
    private static Singleton uniqueInstance;
    private Singleton() {}
    
    public static synchronized Singleton getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}
cs


한 스레드가 메소드 사용을 끝나기 전까지 다른 스레드는, 작업이 끝날 때 까지 기다려야 합니다. 따라서 두 스레드 이상이 동시에 실행시키는 일이 없어집니다.


그러나, 동기화가 꼭 필요한 시점은 이 메소드가 시작되는 때 뿐입니다. uniqueInstance 변수에 Singleton 인스턴스를 대입하고 난 뒤, 다시 동기화 상태로 유지시킬 필요가 없습니다. 불필요한 오버헤드만 증가시킵니다. 성능이 100배 정도 저하될 수도 있습니다.


속도가 그리 중요치 않다면, 가장 간단한 동기화 해결방법입니다.


2. 인스터스를 처음부터 생성한다.

1
2
3
4
5
6
7
8
public class Singleton {
    private static Singleton uniqueInstance = new Singleton();
    private Singleton() {}
    
    public static Singleton getInstance() {
        return uniqueInstance;
    }
}
cs


애플리케이션에서 반드시 Singleton 의 인스턴스를 생성하고, 항상 사용한다면 처음부터 만드는 것도 좋은 방법입니다. 클래스가 로딩될 때, JVM에서 Singleton의 인스턴스를 생성해줍니다.


3. DCL(Double-Checking Locking) 을 써서 getInstance()에서 동기화되는 부분을 줄인다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
    private volatile static Singleton uniqueInstance;
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}
 
cs


인스턴스가 생성되어 있는지 확인합니다. 생성되지 않았을 때만 동기화를 시작합니다.

동기화 블록에 들어온 후에도, 또 다른 스레드가 블록을 끝냈을 지도 모르니, 다시 한 번 변수가 null 인지 확인합니다.

그 다음 인스턴스를 생성합니다.


자바의 Volatile 의 의미는 무엇인가?

Volatile 키워드를 붙이면, CPU 캐시가 아닌 컴퓨터의 메인 메모리에 저장하거나 읽습니다.
Volatile 키워드는 여러 쓰레드에서 사용하는 변수의 변화할 때, 모든 쓰레드에게 그 값을 보장해줍니다.
Volatile 키워드를 붙이지 않는 변수는 쓰레드에서 메인 메모리의 값을 그대로 작업하지 않습니다. 그 값을 CPU 캐시에 복사하여 작업합니다. 그러나 이 때, 변화한 값을 메인 메모리에 다시 써주는 것을 보장해주지 않습니다. 이 때 다시, 다른 스레드가 한 스레드에서 작업한 값을 가져오지 못하는 오류가 발생합니다. 즉, 동기화가 되지 않게 됩니다.


댓글