디자인 패턴 - 1. 싱글턴 패턴

크리링·2024년 11월 5일
0

디자인패턴

목록 보기
1/5
post-thumbnail

싱글턴 패턴

클래스 인스턴스를 하나만 만들고, 그 인스턴스로의 전역 접근을 제공

깃허브 코드



예시 상황

초콜릿 공장에서 초콜릿을 끓이는 장치를 컴퓨터로 제어한다.
보일러는 초클릿이 비어있을때만 돌아가고 비어있으면 초콜릿을 넣는다. 오류가 생길시 500갤런의 초콜릿을 그냥 흘러보낼 수 있다.

public class ChocolateBoiler {
    private boolean empty;
    private boolean boiled;
    private ChocolateBoiler(){
        empty = true;
        boiled = false;
    }
    public void fill() {
        if (empty){
            empty = false;
            boiled = false;
            // 보일러에 우유와 초콜릿을 혼합한 재료를 넣음
        }
    }
    public void drain() {
        if (!empty && boiled) {
            empty = true;
        }
    }
    public void boil() {
        if (!empty && !boiled) {
        }
    }
}

최근 반영한 코드에서 멀티 스레드를 사용하도록 초콜릿 보일러 컨트롤러를 최적화 시켰는데 500갤런이나 되는 우유와 초콜릿이 흘러넘쳤다.



해결

1. 동기화 선언

public class Singleton {
    private static Singleton singleton;
	
	// 기타 변수

	private Singleton() {}

	public static synchronized Singleton getInstance() {
		if (uniqueInstance == null) {
			uniqueInstance = new Singleton();
		}
		return uniqueInstance;
	}
	
	// 기타 메소드
}

하지만 getInstance()의 속도가 중요하다면 문제가 병목을 야기할 수 있다.
(동기화 선언하면 성능이 100배 정도 저하된다.)



2. JVM 로딩시만 인스턴스 생성

public class Singleton {
	private static Singleton uniqueInstance = new Singleton();

	private Singleton() {}

	public static synchronized Singleton getInstance() {
		return uniqueInstance;
	}
}

위의 동기화 속도 저하 문제는 똑같이 해결되지 않는다.



3. DCL (Double-Checked Locking)

public class Singleton {
	private static Singleton uniqueInstance;

	private Singleton() {}

	public static Singleton getInstance() {
		if (uniqueInstance == null) {
			synchronized (Singleton.class) {
				if (uniqueInstance == null) {
					uniqueInstance = new Singleton();
				}
			}
		}
	}
}

DCL(Double-Checked Locking)을 사용하면 인스터스가 생성되어 있는지 확인한 다음 생성되어 있지 않았을 때만 동기화 가능(처음만 동기화)



그렇다면 가장 효율적인 DCL을 적용해서 초기 초콜릿 보일러 코드를 변경한다면


public class ChocolateBoiler {
	
    // 인스턴스 변수
    
    private static ChocolateBoiler uniqueInstance;

	private ChocolateBoiler(){
		empty = true;
		boiled = false;
	}

	public static ChocolateBoiler getInstance(){
		if (uniqueInstance == null) {
			synchronized (ChocolateBoiler.class) {			// 클래스 수준 동기화 ChocolateBoiler 클래스에 모니터 락
				if (uniqueInstance == null) {
					uniqueInstance = new ChocolateBoiler();
				}
			}
		}
		return uniqueInstance;
	}
    
    // 인스턴스 메소드

}



synchronized (ChocolateBoiler.class)

  • 인스턴스 메소드 : 객체가 있어야만 호출할 수 있는 메소드
    boiler.boil();
  • 클래스 메소드 (static 메소드) : 클래스 자체에 종속된 메소드 -> 객체가 없어도 호출할 수 있는 메소드
    Math.random()
  • 인스턴스 레벨 동기화
    • synchornized (this)처럼 객체 수준에서 동기화를 걸면, 해당 객체만 동기화
    • 같은 클래스로부터 생성된 다른 객체들이 있다면 별개 동작
    • 예시
    public synchronized void methodA(){
    		// 해당 인스턴스 에만 동기화
    }
  • 클래스 레벨 동기화
    • synchronized (ChocolateBoiler.class) 처럼 클래스 자체에 동기화를 걸면, 해당 클래스에서 만들어진 모든 객체에 대해 동기화 적용
    • 객체와 관계 없이 클래스 전체에 대해 동기화
    • 예시
    public static synchronized void staticMethod(){
    		// 모든 ChocolateBoiler 객체에서 동기화
    }



0개의 댓글