ABOUT ME

Today
Yesterday
Total
  • Singleton Pattern이란?
    TECH/Java 2022. 12. 16. 13:55

    Singleton Design Pattern

    한 클래스에서 여러 객체가 생성될 수 없도록 하는 방법.

     

    Singleton 사용 사례

    로깅, 데이터베이스 등

     

    Singleton 클래스가 되려면?

    1. 반드시 하나의 인스턴스만 갖는다.
    2. 인스턴스는 글로벌 범위에서 접근할 수 있어야 한다.

    Singleton 클래스를 만드는 방법

    Eager Initialization 방식, Lazy Initialization 크게 두 방식이 있다.

    1. Eager Initialization
      • 클래스의 실제 사용 여부와 무관하게 객체는 무조건 생성된다. 간단하게 구현할 수 있다는 장점이 있는 반면, 실제로 사용되지 않아도 객체를 생성한다는 단점이 있다.
    2. Lazy Initialization
      • 실제로 객체가 필요한 시점에 생성된다.

     

    Singleton 클래스 구현

    Eager Initialization

    • 클래스가 로드되는 시점에 JVM에서 static initializer를 실행하기 때문에 thread-safe하다. singleton 객체가 가벼울 경우 이 방법을 사용할 수 있다.
    class Foo {
    	private static Foo foo = new Foo();
        
        // 생성자를 통한 객체 생성을 방지하기 위해 생성자를 private으로 설정한다.
        private Foo() {}
        
        public static Foo getInstance() {
        	return foo;
        }
    }

     

     

    Lazy Initialization

    version 1

    class Foo {
      private static Foo foo;
      private Foo() {}
      
      public static Foo getInstance() {
        if (foo == null) foo = new Foo();
        return foo;
      }
    }

    getInstance를 호출하는 시점에 foo 객체가 생성된다.

    문제: 여러 스레드에서 동시에 getInstace를 호출할 경우 thread safe하지 않을 수 있다. (여러 foo 객체가 생성될 수 있다.)

     

    version 2

    class Foo {
      public static Foo foo;
      private Foo() {}
      
      public static synchronized Foo getInstance() {
        if (foo == null) foo = new Foo();
        return foo;
      }
    }

    synchronized 키워드를 활용하여 동시에 한 스레드만 getInstance()를 호출할 수 있도록 한다.

    문제: singleton 객체를 생성할 때마다 synchronized를 호출하는 것은 매우 비싼 연산이고, 프로그램의 성능을 저하시킬 수 있다.

    하지만, 프로그램 내에서 getInstance()의 성능이 중요하지 않을 경우, 이 방법을 사용하면 매우 간단하고 깔끔한 해결책이 될 수 있다.

     

    version 3

    class Foo {
      private static volatile Foo foo = null;
      private Foo() {}
      
      public static Foo getInstance() {
        if (foo == null) {
          // 쓰레드 세이프하게 만들기 위해
          synchronized (Foo.class) {
            
            // 여러 쓰레드가 위의 단계를 거쳤을 수 있으므로 한번 더 확인한다.
            if (foo == null) foo = new Foo();
          }
        }
        return foo;
      }
    }

    객체를 volatile로 선언하여 singleton 객체가 초기화 될 때 멀티쓰레드에서 객체를 올바르게 제공할 수 있도록 했다.

    이 방식은 synchronized를 부를 때마다 발생하는 오버헤드를 현저하게 줄여준다.

     

    출처

    https://www.geeksforgeeks.org/singleton-design-pattern-introduction/

    https://www.geeksforgeeks.org/singleton-design-pattern/

Designed by Tistory.