Design Pattern:: 싱글톤 패턴(Singleton)

류영준·2022년 9월 22일
0

Architecture

목록 보기
2/2
post-thumbnail
post-custom-banner

개념

  • 클래스의 인스턴스를 단 하나만 생성하고 어디서든 그 인스턴스를 참조할 수 있도록 하는 디자인 패턴
  • 정리하자면, 프로그램 시작부터 종료 시까지 어떤 클래스의 인스턴스가 메모리 상에 단 하나만 존재할 수 있게 하고 이 인스턴스에 대해 어디에서나 접근할 수 있도록 하는 패턴이다.

장점

  • 고정된 메모리 영역을 얻으면서 한 번의 new로 인스턴스를 사용하기 때문에 메모리 낭비를 방지할 수 있다.
  • 싱글톤으로 만들어진 클래스의 인스턴스는 전역이기 때문에 다른 클래스의 인스턴스들이 데이터를 공유하기 쉽다.

단점

  • 싱글톤 인스턴스가 너무 많은 일을 하거나 많은 데이터를 공유시킬 경우에 다른 클래스의 인스턴스들 간에 결합도가 높아져 OCP(개방-폐쇄 원칙)를 위배하게 된다.
    • 이는 객체 지향 설계 원칙에 어긋나기 때문에 수정이 어려워지고 유지보수 비용이 높아질 수 있다.
  • 멀티쓰레드 환경에서 동기화 처리를 안하면 인스턴스가 2개가 생성될 수 있는 가능성이 생긴다.

활용 상황

  • 공통된 객체를 여러 개 생성해야 하는 상황
  • 전역에서 사용될 하나의 객체를 만들어야 하는 상황
    • ex) DBCP(DataBase Connection Pool), 로거(Logger)

구현

  • 싱글톤을 구현하는 6가지 방법이 있다. 1번부터 시작해서 차례대로 단점을 보완하는 방법이라고 생각하면 된다.
  1. Eager initialization
  2. Static block initialization
  3. Lazy initialization
  4. Thread safe initializaion
  5. Double-Checked Locking
  6. Bill Pugh Soluton

1. Eager initialization

  • Eager initialization은 가장 간단한 형태의 구현 방법이다.
  • 이는 싱글톤 클래스의 인스턴스를 클래스 로딩 단계에서 생성하는 방법이다.
  • 이른 초기화라고 한다.

  • 필드에 자기 자신 인스턴스를 가지면서 바로 초기화 시킨다.
  • 이렇게 하게되면 빠르다는 장점이 있지만 큰 단점이 있다.
  • static을 로딩하면서 바로 메모리를 가지게 된다.
  • 애플리케이션에서 해당 인스턴스를 사용하지 않더라도 인스턴스를 생성하기 때문에 낭비가 발생할 수 있다.
  • Exception Handling 예외 처리를 할 수 있는 방법이 없다.

2. Static block initialization

  • Eager Initialization과 유사하지만 static block을 통해서 Exception Handling에 대한 옵션을 제공합니다.

  • 위와 같이 구현할 경우 싱글톤 클래스의 인스턴스를 생성할 때 발생할 수 있는 예외에 대한 처리를 할 수 있지만, Eager initialization과 마찬가지로 클래스 로딩 단계에서 인스턴스를 생성하기 때문에 여전히 단점을 가지고 있다.
  • 인스턴스가 static block 내에서 생성된다.

3. Lazy initialization

  • 이름에 걸맞게, 앞선 두 방식과는 달리 나중에 초기화하는 방법

  • global access 한 getInstance() 메서드를 호출할 때에 인스턴스가 없다면 생성한다.
    • getInstance() 호출 이외에는 인스턴스를 생성하지 않는다.
  • 1, 2번에서 안고 있던 문제(사용하지 않았을 경우에는 인스턴스가 낭비)에 대해 어느정도 해결책이 된다.
  • 그러나 멀티쓰레드 환경에서 동기화 문제가 발생한다.
  • 만약 인스턴스가 생성되지 않은 시점에서 여러 쓰레드가 동시에 getInstance() 를 호출한다면 수많은 인스턴스가 생성되어 싱글톤 패턴에 위반하는 문제점이 야기될 수 있기 때문에 thread-safe 하지 않다.

4. Thread safe initializaion

  • 3번의 문제를 해결하기 위한 방법으로, getInstance() 메서드에 synchronized 를 걸어두는 방식이다.
  • synchronized 키워드는 임계 영역(Critical Section)을 형성해 해당 영역에 오직 하나의 쓰레드만 접근 가능하게 해준다.

  • 위와 같은 방식으로 구현한다면 getInstance() 메서드에 진입하는 쓰레드가 하나로 보장받기 때문에 멀티 쓰레드 환경에서도 동작하게 된다.
  • 그러나 synchronized 자체에 대한 비용이 크기 때문에 싱글톤 인스턴스 호출이 잦은 애플리케이션에서는 느리고 성능이 떨어지게 된다.

5. Double-Checked Locking

  • 4번의 문제점을 해결하기 위해 고안된 방식
  • getInstance() 메소드 수준에 lock을 걸지 않고 instance가 null일 경우에만 synchronized가 동작하도록 한다.

  • Null 체크를 synchronized 블록 밖에서 한 번, 안에서 한 번, 총 두 번 실행
  • 밖에서 하는 체크는 인스턴스가 있는 경우 빠르게 리턴하기 위해서
  • 안쪽에서 하는 체크는 인스턴스가 생성되지 않는 경우 하나의 인스턴스만 생성하기 위해

6. Bill Pugh Solution

  • Doublc-Checked Locking 방법이 synchronized 의 성능 저하를 보안할 수는 있지만 synchronized 를 치우고싶고 Indent가 마음에 들지 않기 때문에 Bill Pugh 라는 사람이 고안한 방식이다.
  • inner static helper class를 사용하는 방식이다.

  • SingletonHelper 클래스는 BillPughSingleton 클래스가 Load 될 때도 Load 되지 않다가 getInstance() 가 호출됐을 때 비로소 JVM 메모리에 로드되고 인스턴스를 생성하게 된다.
  • Thread safe, Lazy Loading이 가능
  • 구현 간단

참조

https://devmoony.tistory.com/43

https://dailyheumsi.tistory.com/149?category=855210

https://readystory.tistory.com/116

https://www.youtube.com/watch?v=C6CczyrkYXU

https://yaboong.github.io/design-pattern/2018/09/28/thread-safe-singleton-patterns/

https://kdhyo98.tistory.com/70?category=971166

profile
Backend Developer
post-custom-banner

0개의 댓글