JAVA

[JAVA] 싱글턴 패턴

집한구석 2021. 6. 5. 16:15
728x90

정의 및 특징

  • 객체의 인스턴스가 오직 1개로만 만들어져서 공유하는 패턴 
  • 하나의 인스턴스를 메모리에 등록해서 여러 스레드가 동시에 해당 인스턴스를 공유하여 사용하게끔 할 수 있어서, 요청이 많은 곳에서 사용하면 효율을 높임 (전역으로 사용되는 인스턴스이기 때문)
  • 싱글턴을 만들때 동시성(Concurrency) 문제를 고려해야함
  • 내부 상태를 변경하기가 어려움 (상태 넣기가 어려움)

싱글턴 패턴 구현

싱글턴패턴 기본구현은 static영역에 객체 instance를 미리 하나 올려서 getInstance() 메서드를 통해서만 조회할 수 있게 구현을 하며, 생성자를 private으로 막아 new 키워드로 객체인스턴스를 생성못하게 막음, 그외 구현 방식은 여러가지가 있음

싱글턴 패턴 구현 방법

  • Eager Initialization
  • Synchronized 
  • Double Checking Locking
  • Enum
  • LazyHolder

Eager Initialization

public class Singleton {

  private static Singleton INSTACNE = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return INSTACNE;
  }
}
  • static 키워드의 특징을 이용해서 클래스 로더가 초기화 하는 시점에서 정적 바인딩(컴파일 시점에서 성격이 결정됨)을 통해 해당 인스턴스를 메모리에 등록해서 사용
  • 멀티쓰레드 환경에서 쓰레드 세이프가 보장이 되지 않음 (다음 단계에서 Synchronized 패턴이 추가된 이유)

Synchronized

public class Singleton {

  private static Singleton INSTACNE = new Singleton();

  private Singleton() {
  }

  public static synchronized Singleton getInstance() {
    if (INSTACNE == null) {
      INSTACNE = new Singleton();
    }
    return INSTACNE;
  }
}
  • synchronized 키워드를 이용하여 초기화한 방식, 동기화를 지정하여 처리함 
  • 인스턴스가 필요한 시점에 요청 하여 동적 바인딩(런타임시에 성격이 결정됨)을 통해 인스턴스를 생성하는 방식
  • 동기화 블록을 매번 걷혀야하기 때문에 성능 이슈가 발생함 (synchronized시 속도가 100배 정도 감소한다고함)
  • 성능 이슈로 인해서 Double Check Locking 방식이 생김

Double Check Locking

public class Singleton {

  private volatile static Singleton INSTANCE = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    if (INSTANCE == null) {
      synchronized (Singleton.class) {
        if (INSTANCE == null) {
          INSTANCE = new Singleton();
        }
      }
    }
    return INSTANCE;
  }
}
  • instance가 생성되지 않은 경우에만 동기화 처리를 하는 방식
  • 오브젝트 재배치 문제로 인하여 volatile 키워드를 선언해야함

Enum

public enum Singleton {
  INSTANCE;
}
  • Enum 자체가 instance가 쓰레드 세이프함
  • 구현이 매우 간단함 
  • Enum내에 다른 메소드가 있는 경우 해당 메소드가 쓰레드 세이프 관련해서 처리해야함 

Lazy Holder

public class Singleton {

  private Singleton() {
  }

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

  public static class LazyHolder {

    private static Singleton INSTANCE = new Singleton();
  }
}
  • 가장 완벽한 싱글턴 구현 방법
  • static영역에 초기화를 하지만 객체가 필요한시점까지 초기화를 미루는 방식
  • 클래스의 getInstance() 메서드에서 LazyHolder.INSTANCE를 참조하는 순간 클래스가 로딩되며 초기화가 진행된다. Class를 로딩하고 초기화하는 시점은 스레드세이프를 보장하기 때문에 volatile이나 synchronized 같은 키워드가 없어도 스레드세이프하며 성능이슈가 없음

싱글턴 패턴 문제점

  • 구현하는 코드 자체가 많이 들어감
  • 의존관계상 클라이언트가 구체 클래스에 의존 (DIP 위반)
  • 구체 클래스에 의존해서 OCP 원칙을 위반할 가능성이 높음
  • private 생성자로 자식클래스 만들기 어려움, 유연성이 떨어짐

'JAVA' 카테고리의 다른 글

[JAVA] Map / getOrDefault  (0) 2021.06.10
[JAVA] 예외(Exception)  (0) 2021.06.07
[JAVA] JVM 구조  (0) 2021.06.03
[JAVA] Enum  (0) 2021.06.02
[JAVA] GC의 종류  (0) 2021.05.23