개발

싱글톤 패턴 (Singleton Pattern)

simba 2020. 8. 12. 16:20
순서
  1. 싱글톤 패턴의 개념
  2. 싱글톤 패턴의 장점
  3. 자바의 싱글톤 패턴 구현

 

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

소프트웨어 디자인 패턴에서 싱글턴 패턴을 따르는 클래스는, 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴한다. 이와 같은 디자인 유형을 싱글턴 패턴이라고 한다. 주로 공통된 객체를 여러개 생성해서 사용하는 DBCP(DataBase Connection Pool)와 같은 상황에서 많이 사용된다.

- 하나의 자원을 모두가 공유해서 사용하는 경우

- 싱글톤 패턴이란 전역변수를 사용하지 않고, 객체를 하나만 생성하여 생성된 객체를 어디에서든 참조 할 수 있도록 하는 패턴을 말한다.

- 하나의 인스턴스를 메모리에 등록해서 여러 스레드가 동시에 해당 인스턴스를 공유하여 사용하게 끔하여, 효율을 높일 수 있다.

- 동시성(Concurrency) 문제를 고려하여 싱글톤 설계를 해야한다.

 

 

 

2. 싱글톤 패턴을 쓰는 이유

- 고정된 메모리 영역을 얻으면서 한번의 new로 인스턴스를 사용하기 때문에 메모리 낭비를 방지할 수 있다.

- 전역 인스턴스이기 때문에 다른 클래스의 인스턴스들이 데이터를 공유하기 쉽다.

- DBCP(DataBase Connection Pool)처럼 공통된 객체를 여러개 생성해서 사용해야하는 상황에서 사용

 

 

3.자바의 싱글톤 패턴 구현

Eager Initialization(이른 초기화, Not-Thread-safe)


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

private Singleton() {}

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

 

이른 초기화는 static을 이용하여 초기화 하는 시점에 정적바인딩을 통해 인스턴스를 메모리에 등록해서 사용하는 것이다. 이른 초기화 방식은 스레드 세이프를 하지 않는다. 하지만 싱글톤 패턴은 동시성 문제를 고려해야하고, 멀티스레드일 경우 문제가 생기기때문에 Thread-safe가 필요하다.

 

( static 변수 - 객체가 생성되기 전 클래스가 메모리에 로딩될 때 만들어져 초기화가 한번만 실행된다.

                   - 프로그램 시작 ~ 종료까지 없어지지 않고 메모리에 계속 상주하며 클래스에서 생성된 모든 객체에서 참조할 수 있다)

 

 

 

Lazy Initialization with synchronized (동기화 블럭, Thread-safe)


public class Singleton {

private static Singleton uniqueInstance;

private Singleton() {}

// Lazy Initailization
public static synchronzied Singleton getInstance() {
      if(uniqueInstance == null) {
         uniqueInstance = new Singleton();
       }
    return uniqueInstance;
     }
}

 

게으른 초기화 방식은 synchronzied를 이용하여 메서드에 동기화 블럭을 지정해서 Thread-safe를 보장한다.

컴파일 시점에 인스턴스를 생성하는 것이 아니라 인스턴스가 필요한 시점에 요청하여 동적바인딩을 통해 인스턴스를 생성한다.

 

 

Lazy Initialization. Double Checking Locking(DCL, Thread-safe)

  • 인스턴스가 생성되지 않은 경우에만 동기화 블럭이 실행되게 한다.

public class Singleton {
private volatile static Singleton uniqueInstance;
private Sigleton() {}

// Lazy Initialization. DCL
public Singleton getInstance() {
    if(uniqueInstance == null) {
        synchronized(Singleton.class) {
           if(uniqueInstance == null) {
               uniqueInstance = new Singleton();
            }
         }
     }
     return uniqueInstance; 
     }
}

 

volatile 키워드를 사용하여 멀티스레딩을 쓰더라도 uniqueInstance 변수가 싱글톤인스턴스로 초기화 되는 과정이 올바르게 진행될 수 있다.

 

 

 

Lazy Initialization. LazyHolder(게으른 홀더, Thread-safe)

  • 가장 많이 사용되는 싱글톤 구현 방식
  • volatile이나 synchronized 키워드 없이도 동시성 문제를 해결하기 때문에 성능이 뛰어나다

public class Singleton {
  private Singleton() {}

  /**
  * static member class
  *내부클래스에서 static변수를 선언해야하는 경우 static 내부 클래스를 선언해야만 한다.   
  * static 멤버, 특히 static 메서드에서 사용될 목적으로 선언
  */
   private static class InnerInstanceClazz() {
   // 클래스 로딩 시점에서 생성
   private static final Singleton uniqueInstance = new Singleton();
   }
 
   public static Singleton getInstance() {
     return InnerInstanceClazz.instance;   
   }
}

 

- JVM의 클래스 로더 메커니즘과 클래스의 로드 시점을 이용하여 내부 클래스를 통해 생성 시킴으로써 쓰레드 간의 동기화 문제를 해결한다. 가장 대표적인 방법이다.

 

- 싱글턴 클래스에는 InnerInstanceClazz 클래스의 변수가 없기 때문에, static 멤버 클래스더라도 클래스 로더가 초기화 과정을 진행할 때 InnerInstanceClazz를 초기화 하지 않고 getInstance()메서드를 호출할 때 초기화가 된다.

InnerInstanceClazz 내부 인스턴스는 static 이기 때문에 클래스 로딩 시점에 한번만 호출된다는 점을 이용한것이며, final을 써서 다시 값이 할당되지 않도록 한다

 

 

 

References

https://medium.com/webeveloper/%EC%8B%B1%EA%B8%80%ED%84%B4-%ED%8C%A8%ED%84%B4-singleton-pattern-db75ed29c36