mojo's Blog

Singleton Pattern 본문

Design Patterns

Singleton Pattern

_mojo_ 2024. 6. 9. 13:31

Singleton Pattern

 

목적: 시스템 내에서 클래스 객체가 하나만 허용되도록 함

사용시기

- 클래스의 객체가 정확히 하나 필요함

- 단일 객체에 대한 제어된 접근이 필요함

 

※ One of a Kind Objects

오직 하나의 객체로 관리되어야 할 것들

- Window Manager

- Thread Pool Manager

- Caches

- Logging

 

하나의 객체 관리가 왜 어려울까?

- Multi-threading 문제

- 글로벌 변수를 생각해보기

 

※ The Skeleton of Singleton

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

1. 생성자는 private 로 선언한다.

     외부에서 객체 생성 금지

2. getInstance() 메서드를 제공한다.

     static => 컴파일 타임에 Singleton 객체를 생성하여 uniqueInstance 할당

     public => 외부에서 단일 객체를 불러오기 위함

 

※ Using Singleton on Multi-threads

두 개의 스레드가 있는 상태에서 getInstance() 메서드를 호출한다고 가정한다.

각 스레드 별로 getInstance() 메서드에 들어왔고, uniqueInstance 가 둘 다 null 인 경우

Singleton 객체를 각 스레드 별로 생성하게 된다.

그리고 서로 다른 Singleton 객체를 리턴함으로써 의도하지 않은 객체를 사용하게 된다.

 

※ Solving the Problem

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

첫번째 방식은 synchronized 키워드를 붙여서 스레드 하나만 접근 가능하도록 한다.

작동이 보장되는 간단한 기술이지만, 잦은 잠금으로 인해 런타임 성능에 작은 영향을 미칠 수 있다.

 

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

두번째 방식은 컴파일 타임때 uniqueInstance 에 Singleton 객체를 할당한다.

항상 클래스를 객체화할 경우 정적으로 객체를 초기화하면 문제가 발생하지 않는다.

 

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

세번째 방식은 uniqueInstance가 null인 경우 synchronized를 안에 걸어줌으로써 한번 더 null을 체크한다.

그리고 uniqueInstance 에 volatile 키워드를 붙인다.

완벽한 솔루션이지만, 성능 문제가 없을 경우에는 두 번 확인한 잠금이 overkill 될 수 있다.

또한 최소한 Java 5를 실행하고 있는지 확인해야 한다.

 

* volatile: 모든 스레드가 항상 같은 공유 변수의 값을 읽어올 수 있도록 보장

   메인 메모리에 해당 객체 값이 갱신되고 이로써 다른 모든 스레드들은 null 이 아닌 공유 변수에 할당된

    객체를 메인 메모리로부터 바로 읽어올 수 있게 됨

 

'Design Patterns' 카테고리의 다른 글

Adapter Pattern  (0) 2024.06.09
Decorator Pattern  (0) 2024.06.09
Builder Pattern  (0) 2024.06.09
Factory Method Pattern & Abstract Factory Pattern  (0) 2024.06.09
Mediator Pattern  (0) 2024.06.06
Comments