카테고리 없음

Singleton

기기디 2024. 3. 16. 16:31

정의

  • 객체의 인스턴스가 오직 1개만 생성되는 패턴을 말한다.
  • 클래스의 인스턴스가 오직 하나만 생성되는것을 보장하고 해당 인스턴스에 전역적으로 접근할 수 있는 방법을 제공하는 패턴이다.

싱글톤의 단점

  • private생성자를 가지고 있어 상속이 불가능하다
  • 테스트하기 어렵다
    • 생성방식이 제한적이고 Mock객체로 대체하기 어려우며 동적으로 객체를 주입하기도 어렵기 때문이다.
  • 서버환경에서 싱글톤이 1개만 생성됨을 보장하지 못한다.
    • 클래스로더를 어떻게 구성하느냐에 따라서 싱글톤이지만 1개이상의 객체가 생성될 수 있다. 자바언어를 이용한 싱글톤 기법은 서버환경에서 싱글톤을 보장하지 않는다. 분산환경에서도 싱글톤을 보장하기 어렵다
  • 전역상태로 인한 문제
    • 싱글톤의 스태틱메소드를 사용하여 언제든지 해당 객체를 사용하도록 전역설정을 할 수 있다. 전역상태는 객체지향 프로그래밍에서 권장하지 않는다.
  • Spring에서는 컨테이너를 통해 직접 객체를 싱글톤으로 관리함으로써 객체지향스럽게 개발이 이루어지도록 해주었다.

(안전한)싱글톤 클래스,인스턴스를 구현하는 방법들

  • 초기화 시점에 따라서 나눌 수 있고 thread safe보장여부에 따라서 나눠볼 수 있습니다.

Eager Initialization - 이른 초기화

  • 장점
    • 가장 간단한 형태의 싱글톤 구현방법이다.
    • 클래스의 인스턴스를 클래스 로딩시점에 생성하는 방법으로 thread safe를 보장한다.
  • 단점
    • Exception에 대한 핸들링을 제공하지 않는다.
    • 인스턴스를 무조건 미리 만들어놓기에 실제로 사용되지 않을경우 낭비다.

Static Block Initailization - 정적 블럭 초기화

  • 이른 초기화와 유사하지만 static block에 try-catch구문을 사용하여 Exception handling이 가능하다는 차이가 존재한다.

Lazy Initailization - 게으른 초기화

  • 이른 초기화처럼 클래스로딩시점에 초기화를 하는것이 아닌 해당 클래스의 인스턴스를 만드는 메소드를 호출할 때 인스턴스가 없다면 생성하는 방식이다.
  • 장점
    • 이른 초기화의 문제점인 사용하지 않았을 경우 인스턴스가 낭비되는 문제를 해결한다
  • 단점
    • thread safe를 보장하지 않는다. 클래스의 인스턴스가 없는 상황에서 여러 쓰레드가 동시에 인스턴스를 만드는 메소드를 호출한다면 여러개의 인스턴스가 생성되어 싱글톤을 보장하지 못한다.

Lazy initialization with synchronized - 동기화블럭을 사용한 게으른 초기화

  • 기존 게으른 초기화방법에 synchronized block을 사용하여 thread safe를 보장하는 방법이다.
  • private static으로 해당 클래스의 인스턴스변수를 선언하고 private생성자를 사용하여 외부에서 생성을 막고 인스턴스 생성 메소드에 synchronized키워드를 사용하여 thread safe하게 만든다.
  • 단점
    • synchronized를 사용함에 따라 큰 성능저하를 유발하여 권장하지 않는 방법이다. 싱글턴 인스턴스를 자주 호출하는 어플리케이션이라면 더욱 더 성능이 떨어진다.

Lazy initialization + Double-checked locking

  • synchronized를 바로 사용하는것이 아닌 조건문으로 인스턴스의 존재여부를 체크 이후 synchronized를 사용하여 성능저하를 줄이는 방법이다.
  • 성능저하를 근본적으로 해결하는 방법은 아니다.
  • volatile예약어를 사용하면 멀티스레드환경에서도 instance변수가 singleton인스턴스로 초기화되는 과정이 올바르게 진행되도록 할 수 있다.

Initialization on demand holder idiom - holder에 의한 초기화

  • inner static helper class를 사용하는 방식으로 volatile이나 synchronized키워드 없이 동시성 문제를 해결한다.
  • 클래스안에 클래스를 두어 홀더역할을 하게하여 JVM의 Class Loader메커니즘과 Class가 로드되는 시점을 이용한 방법이다.
  • JVM의 클래스 초기화 과정에서 보장되는 원자적특성을 이요하여 싱글톤의 초기화 문제에 대한 책임을 JVM에 위임한다.
  • 방법
    • private inner static class를 만들어 싱글톤 인스턴스를 갖게 한다.
    • inner class는 Singleton클래스가 Load될때도 Load되지 않다가 getInstance()가 호출됐을때 JVM메모리에 로드되고 인스턴스를 생성한다.
    • holder안에 선언된 인스턴스가 static이기 때문에 클래스 로딩시점에 한번만 호출될것이며 final을 사용해 다시 값이 할당되지 않도록 하는 방법이다
  • 제일 많이 사용되는 Singleton클래스 사용방법이다.

Enum Singleton

  • 정의
    • Java에서 리플렉션이나 직렬화과정에서 싱글톤이 파괴될 수 있기에 이를 방지하기 위하여 enum을 사용하는 방법이다. enum클래스의 인스턴스 생성은 기본적으로 Thread Safe를 보장하기 때문이다.
  • 주의해야할점
    • 만들려는 싱글톤 클래스가 enum클래스 말고 다른 클래스를 상속받아야하는 경우 다중상속이 불가능하기에 사용하기 어렵다.

싱글톤을 보장하기 위한 Spring Configuration

  • 각각 다른 빈에서 의존성 주입의 대상이 같은 클래스(ex>다른 서비스가 같은 레포지토리)일경우 싱글톤이 깨지는 상황이 된다.
  • 이런 경우 싱글톤이 깨지지 않도록 스프링은 클래스의 바이트코드를 조작하는 라이브러리를 사용한다.(런타임시?) 해당내용은 Configuration Annotation안에 있다.
  • Configuration Annotation또한 빈으로 등록된다. Configuration클래스는 그대로 빈으로 등록되지 않고 스프링이 CGLIB이라는 바이트코드조작 라이브러리를 사용하여 Configuration클래스를 상속받은 임의의 다른 클래스를 만들고 그 다른 클래스를 빈으로 등록한다.
  • 이런 과정을 통해서 @Bean이 등록된 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어진다.