[Design Pattern] Singleton 패턴이란

zl존석동

·

2021. 12. 31. 21:05

소프트웨어 디자인 패턴 중 하나인 싱글톤 패턴에 대해 공부하자!


Singleton 패턴이란?

 

 

 

 

 

 

 

Singleton 패턴이란?

 

인스턴스를 필요할 때마다 계속해서 만들지 않고 메모리 내에 단 한번만 생성한 다음 재사용하기 위한 디자인 패턴이다.

 

 

 

왜 사용할까??

 

다른 클래스의 인스턴스들에서 사용되고 공유되어야 하는 단 하나의 객체가 필요할 때 사용한다.

 

한번의 객체 생성으로 같은 것을 재사용 할 수 있기 때문에 메모리 낭비를 방지할 수 있다.

 

 

 

 

문제점

 

싱글톤 객체가 너무 많은 데이터를 공유시킬 경우 클래스간 결합도가 높아져 유지보수와 테스트가 어려워질 수 있다.

 

멀티 스레드 환경에서 동기화 처리를 하지 않으면 하나의 인스턴스가 보장되지 않을 수 있다. 

 

 

 

 

사용법(Java)

 

 

기본 방식으로의 구현

 

public class MySingleton {

  private static MySingleton instance;
  
  private MySingleton() {
    System.out.println("My Singleton 초기화!!" + (int)(Math.random()*1000));
  }

  public static MySingleton getInstance() {
    if(instance == null) {
      instance = new MySingleton();
    }
    return instance;
  }
}

 

 

생성자를 private으로 두어 외부에서 초기화 할 수 없게 하면서 static 을 통해 단 한번만 객체가 생성되게끔 한 것이다.

 

위의 클래스를 어디서 몇번을 호출해도 최초에 초기화 된 객체를 불러와 사용하게 된다.

 

 

 

Thread Safe?

 

 

저대로는 100% 보장할 수 없다.

 

위의 싱글톤 클래스를 아래의 코드처럼 멀티 스레드 환경에서 호출하게 될 경우 단 하나임을 보장해주지 못한다.

 

 

public class MultiMain {

  public static void main(String[] args) {
    // TODO Auto-generated method stub
    MultiThread[] mt = new MultiThread[2];
    for (int i = 0; i < 2; i++) {
      mt[i] = new MultiThread();
      mt[i].start();
    }
  }
}
class MultiThread extends Thread {
  MultiThread() {}

  public void run() {
    try {
      MySingleton mySingleton = MySingleton.getInstance();
      System.out.println(mySingleton);
      /* Source */
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

 

 

 

두 개의 스레드가 각각 순차적이라는 보장이 없기 때문에 

 

instance == null 조건을 둘 다 만족하게 되어버려 인스턴스가 두 개 생성된다.

 

 

 

Thread Safe! 

 

다중 스레드에서도 단 하나임을 보장해줄 수 있도록 싱글톤 클래스를 구현해보자.

 

 

1. Synchronized

 

Synchronized 키워드를 붙이면 하나의 스레드가 해당 메소드를 호출하여 종료시킬 때 까지

 

다른 스레드들이 접근할 수 없도록 Lock 을 걸어버린다.

 

단어 그대로 스레드를 동기화 시켜버리기 때문에 처리 성능 저하가 심하다.

 

public class MySyncSingleton {
  private static MySyncSingleton instance;

  private MySyncSingleton() {
    System.out.println("My Sync Singleton 초기화!!" + (int)(Math.random()*1000));
  }

  public static synchronized MySyncSingleton getInstance() {
    if (instance == null) {
      instance = new MySyncSingleton();
    }
    return instance;
  }
}

 

 

 

2. Static 초기화

 

Thread-Safe 하지 않은 방법과 유사하지만

 

필요한 시점에 인스턴스로 생성되는 것이 아니라 미리 생성되는 것이 차이점이다.

 

미리 생성되기 때문에 Thread-Safe 하고 반드시 사용 될 것으로 예상이 된다면 가장 좋은 방법이지 않을까 생각한다.

 

public class MyStaticSingleton {
  //
  private static MyStaticSingleton instance = new MyStaticSingleton();

  private MyStaticSingleton() {
    System.out.println("My Singleton 초기화!!" + (int) (Math.random() * 1000));
  }

  public static MyStaticSingleton getInstance() {
    return instance;
  }
}

 

 

 

3. Lazyholder Singleton

 

싱글톤 클래스 안에 클래스를 두어 JVM에서 클래스가 단 하나만 로드됨을 이용하는 방법이다.

 

MyLazyHolderSingleton 싱글톤 클래스가 로드될 때 LazyHolder 클래스는 로드되지 않지만 최초로 getInstance()

 

메소드가 호출되는 시점에 LazyHolder 클래스가 로드되게 된다.

 

LazyHolder 클래스 로드 시점에 INSTANCE 가 단 한번만 생성되며 값이 재할당 될 수도 없다.

 

이 방법으로 다중스레드에도 안전하면서 객체가 필요해지는 시점까지 초기화를 지연시킬 수 있게 된다. 

 

public class MyLazyHolderSingleton {

  private MyLazyHolderSingleton() {
    System.out.println("My Sync Singleton 초기화!!" + (int) (Math.random() * 1000));
  }

  public static MyLazyHolderSingleton getInstance() {
    return LazyHolder.INSTANCE;
  }

  private static class LazyHolder {
    private static final MyLazyHolderSingleton INSTANCE = new MyLazyHolderSingleton();
  }
}

 

 

 

 

 

ref

 

https://javaplant.tistory.com/21

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

[Design Pattern] 설명하기 쉽지 않은 MVC Pattern  (0) 2021.12.23