korean IT student

싱글톤 패턴(Singleton Pattern) 본문

디자인패턴(java)

싱글톤 패턴(Singleton Pattern)

현창이 2021. 11. 1. 16:16

싱글톤 패턴(Singleton Pattern) 이란?

  • 인스턴스를 오직 한개만 제공하는 클래스
  • 시스템 런타임, 환경 세팅에 대한 정보 등, 인스턴스가 여러개 일 때 문제가 생길 수 있는 경우가 있다. 인스튼서를 오직 한개만 만들어 제공하는 클래스가 필요
  • 즉, 하나의 인스턴스를 메모리에 등록해서 여러 스레드가 동시에 해당 인스턴스를 공유하여 사용하면 요청이 많은 곳에서 효율을 높일 수 있다.

 

싱글톤 패턴 예제

 

1. Thread-safe 하지 않은 코드(사용 x)

public class Singleton {
    private static Singleton instance;

// 생성자는 외부에서 호출하지 못하게 private으로 지정
    private Singleton() {} 

// 예를 들어 쓰레드 두개가 동시에 getInstance()를 호출하면 instance가 생성되기 전이라 각각 instance가 생성된다.
    public static Singleton getInstance() {
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

2. 동기화(synchronized)를 사용해 멀티쓰레드 환경에 안전하게 만들기

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
    
}
  • synchronized를 이용한 게으른 초기화 방식, 메서드에 동기화 블럭을 지정해서 Thread-safe를 보장
  • 게으른 초기화 방식이란. 컴파일 시점에 인스턴스를 생성하는것이아니라, 인스턴스가 필요한 시점에 요청하여 동적 바인딩을 통해 인스턴스를 생성
  • 단점은 getInstance() 메서드를 호출하면 Synchronized block(동기화 블록)을 무조권 호출한다. 그래서 성능이 저하될 수 있다. 아래의 단점을 보완한 double checked locking을 이용하자.

3. double checked locking으로 효율적인 동기화 블럭 만들자.

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

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

}
  • 인스턴스가 생성되지 않은 경우에만 동기화 블럭이 실행되게끔 구현
  • volatile 변수를 사용한이유
    • Volatile 변수를 사용하고 있지 않는 멀티 스레드 어플리케이션에서는 작업을 수행하는 동안 성능 향상을 위해 Main Meomry에서 읽은 변수 값을 CPU 캐시에 저장하게 됩니다. 만약에 멀티 스레드 환경에서 스레드가 변수 값을 읽어올  때 각각의 CPU 캐시에 저장된 값이 다르기 때문에 변수 값이 다릅니다.
    • Volatile로 선언된 변수는 메인 메모리에서 CPU의 캐시에 적재되지 않는 변수를 의미하며, 주로 여러 스레드가 동시에 접근할 수 있는 변수를 Volatile로 선언합니다.

4. Enum을 사용하는 방법

public enum Settings5 {

    INSTANCE;

}
  • Enum 인스턴스의 생성은 기본적으로 thread safe하다. 다른 코드가 없어져서 코드가 간단합니다. 하지만 다른 메서드가 enum에 있는경우 개발자가 확인해야한다.
  • 리플렉션, 직렬화, 역직렬화를 사용할때 제 2의 인스턴스가 생성되는것을 막아줍니다.
  • 하지만 다른 클래스를 상속해야하는 경우에는 사용할수 없는 단점이 있습니다.
  • 애플리케이션 구동시 메모리에 올라가기때문에 초기화 지연을 사용하지 못합니다.

5. static inner 클래스를 사용하는 방법

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}


    private static class SingletonHolder {
    	// 클래스 로딩 시점 -> 생성
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

}
  • volatile 이나 synchronized 키워드 없이도 동시성 문제를 해결하기 때문에 성능이 좋다.
  • SingletonHolder 내부 인스턴스는 static 이라 클래스 로딩 시점에 한번만 호출되고, final을 써서 다시 값이 할당되지 않도록 한다.

실무에서는 어떻게 사용하고 있을까?

 

1. 스프링에서 빈의 스코프 중에 하나로 싱글톤 스코프

  • 스프링 빈은 기본적으로 모든 bean을 싱글톤으로 생성하여 관리
  • String객체 wrold 와 world2 값을 비교하면 true

 

2. 자바 java.lang.Runtime

Java 의 Runtime 클래스가 싱글톤으로 구현된 대표적인 클래스

 

 

3. 다른 디자인 패턴(빌더, 퍼사드, 추상 팩토리 등) 구현체의 일부로 사용됩니다.

 

 

 

[참고]- https://webdevtechblog.com/%EC%8B%B1%EA%B8%80%ED%84%B4-%ED%8C%A8%ED%84%B4-singleton-pattern-db75ed29c36

Comments