The Singleton Pattern

2023. 10. 21. 16:28디자인패턴

728x90

문제 배경

오직 하나뿐인 객체를 만들고 싶다.

어떻게 만들 수 있을까?

1. 

public MyClass {
	private MyClass() {}
}

이렇게 만들어버리면 클래스 생성자체를 못한다. 

 

2.

public MyClass {
    private MyClass() {}
    public static MyClass getInstance() {
    	return new MyClass();
    }
}

이렇게 만들면 외부에선 생성하지 못하지만 
내부에선 여러개의 클래스 생성가능

 

 

Singleton Pattern

public class Singleton {
    private static Singleton uniqueInstance;
    // other useful instance variables here
    private Singleton() {}
    public static Singleton getInstance() {
        if (uniqueInstance == null) {
        uniqueInstance = new Singleton();
    	}
    return uniqueInstance;
    }
    // other useful methods here
}

이렇게 만들면 오직 하나뿐인 객체를 만들 수 있다.

Singleton m=Singleton.getInstance();
Singleton n=Singleton.getInstance(); 
/*uniqueInstance 변수가 처음에 만들어진 객체를 가리키고 있으므로 null이 아니게 된다.
그러므로 처음 만들어진 객체를 가리키게 된다.
결국 m,n 둘다 같은 객체를 가리키게 됨.
*/

 

그리고 생성자가 private이고

getInstance메소드는 static 메소드이기 때문에

new singleton(); 으로 생성할 수 없고
singleton.getInstance();로만 생성 가능

 

 

 

멀티스레드 환경에서도 하나의 객체만 만들고 싶을 때

public class Singleton {
    private static Singleton uniqueInstance;
    // other useful instance variables here
    private Singleton() {}
    public static synchronized Singleton getInstance() {
    //synchronized 키워드를 사용하여 getInstance함수가 종료될때까지 다른 스레드는 락에 걸려있게 됨.
        if (uniqueInstance == null) {
        uniqueInstance = new Singleton();
    	}
    	return uniqueInstance;
    }
    // other useful methods here
}

문제점: 처음에 객체를 만들고 난 뒤로는 synchronized 키워드가 필요없다

해결책: double-checked locking을 사용

public class Singleton {
    private volatile static Singleton uniqueInstance; //volatile 키워드를 사용 
    private Singleton() {}
    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {//이 블록 안의 코드만 synchronized 적용,class 동기화
                if (uniqueInstance == null) {
                	uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

* 객체가 null 일 경우에만 synchronized가 실행되도록 하여 객체가 생성된 후에는 synchronized가 실행되지 않는다.

즉, 무분별한 synchronized 호출의 비용을 절약할 수 있다.

 

* volatile 키워드: thread safety 보장 위한 키워드

키워드로 지정을 하지 않으면 각 스레드는 변수의 복사본을 유지해

변수를 변경해도 실제 값은 변경이 안되고 복사본만 변경된다.

volatile 지정된 변수를 변경 시

-> 스레드가 메모리에 저장된 실제 값을 읽어옴(실제로 값 변경)

 

* 동작 과정

1. 첫번째 스레드에서
null인가? -> 그렇다. class 생성자 호출
2. 첫번째 스레드 sleep
3. 두번째 스레드에서 
null인가? -> 그렇다. class 생성자 호출

4. 두번째 스레드 sleep
5. 첫번째 스레드 
null인가 ? -> 그렇다. class 생성
6. 두번째 스레드
null인가 ? -> 첫번째 스레드에서 생성되었으므로 생성x

728x90

'디자인패턴' 카테고리의 다른 글

The Adapter and Facade Patterns  (0) 2023.10.21
The Command Pattern  (1) 2023.10.21
Decorator Pattern  (2) 2023.10.21
The observer pattern  (0) 2023.10.19
Strategy Pattern  (0) 2023.10.18