ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Singleton pattern. 싱글턴
    DesignPattern/Creational Patterns 2013. 10. 31. 13:51

    Singleton pattern. 싱글턴

     

    singleton pattern은 패턴을 사용하는 특정 class의 인스턴스는 오직 하나. 임을 보장한다.

     

    Common Uses.

    - Abstract Factory, Builder, Prototype pattern의 구현에서 singleton을 사용

    - Facade에서 해당 object를 단 하나만 필요로 할 때 사용.

    - State object들이 종종 singleton일 경우가 있다.

    - 또, Singleton은 가끔 전역 변수로 사용되어지는 것이 선호되는데,

    Singleton은 불필요한 변수들과 함께 전역 name space를 더럽히지 않기 때문이고

    lazy allocation이나 initialization을 허용/ 가능하게 하기 때문이다.

     

    UML

    Singleton.png

     

    Example

    Eager initialization

    아래의 방법이 가장 보편적이면서 많이 쓰이는 방법인데,

    Application이 해당 instance를 항상 유지하고 사용할 때 유용하며

    해당 instance를 항상 create하고 사용하면서 드는 비용이 크다고 판단되면 singleton으로 바꾸는 것을 고려해야 한다.

    public class Singleton {
        private static final Singleton INSTANCE = new Singleton();

        private Singleton() {}

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

    이 방법을 사용해서 얻을 수 있는 장점에는

    - 인스턴스는 해당 클래스가 사용되기 전까지 생성되지 않는다는 것.

    -  getInstance()에 synchronize를 사용할 필요가 없다는 것.

    - 모든 thread들이 동일한 인스턴스를 바라보고 있고,

    - 비용이 드는 locking을 사용할 필요가 없기 때문에.

    - final 키워드가 인스턴스가 다시 재정의 될 수 없다는 것을 의미하고, 오직 하나의 인스턴스가 존재함을 보장한다는 것이 있다.

     

    Lazy initialization

    with DCL(Double-checked locking)

    public class SingletonDemo {
            private static volatile SingletonDemo instance = null;

            private SingletonDemo() {       }

            public static SingletonDemo getInstance() {
                    if (instance == null) {
                            synchronized (SingletonDemo .class){
                                    if (instance == null) {
                                            instance = new SingletonDemo ();
                                    }
                            }
                    }
                    return instance;
            }
    }

    이 방법은 문제가 있을 수 있다.

    Singleton 생성자가 실행되기 전에 instance를 참조하여 out-of-order writing이 될 수 있기 때문이다.

    관련된 문제에 관한 자세한 내용과 해법은 DCL과 lazy initialization과 관련된 문서에서 쉽게 찾을 수 있다.

     

    under multi-threaded (but, high cost)

    public class SingletonDemo {
            private static SingletonDemo instance = null;

            private SingletonDemo() {       }

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

    이 방법은 multi-thread 환경에서 간단하고 명쾌하게 해결된 방법이지만 잠재적으로 비싼 비용에 대한 문제를 안고 있다.

     

    Static block initialization

    error-checking과 같은 pre-processing을 사용하는 솔루션에서 아래와 같은 전역 초기화 기법을 사용하기도 한다.

    public class Singleton {
      private static final Singleton instance;

      static {
        try {
          instance = new Singleton();
        } catch (IOException e) {
          throw new RuntimeException("Darn, an error occurred!", e);
        }
      }

      public static Singleton getInstance() {
        return instance;
      }

      private Singleton() {
        // ...
      }
    }

     

    The solution of Bill Pugh

    이 기법은 initialization on demand holder로 알려져 있는 개념인데,
    모든 Java 버전에서 동작하도록 설계되어 있다.

    클래스 초기화를 언어적인 측면에서 보장하도록 설계되어 있기 때문에 모든 Java 버전과 Virtual Machine에서 동작한다.

    중첩된 클래스는 getInstance()가 불리는 시점 이전에 참조될 수 없으며, Class Loader에 의해 먼저 로드되지 않는다.
    따라서 이 기법은 volatile 혹은 synchronized와 같은 키워드가 없어도 thread-safe 하다.

    public class Singleton {
            // Private constructor prevents instantiation from other classes
            private Singleton() { }

            /**
            * SingletonHolder is loaded on the first execution of Singleton.getInstance()
            * or the first access to SingletonHolder.INSTANCE, not before.
            */

            private static class SingletonHolder {
                    public static final Singleton INSTANCE = new Singleton();
            }

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

     

    The Enum way

    Effective Java 2nd(Joshua Bloch) 에서 "single-element enum type은 enums를 지원하는 Java의 어떤 버전에서든 가장 효과적인 싱글턴 구현 방법이다."라고 기술되어 있고, enum을 사용하는 것은 정말 쉬운 방법이고, 다른 방법들을 통해서 피해야 하는 serializable objects에 관련된 문제점들이 없다고 설명하고 있다.

    public enum Singleton {
            INSTANCE;
            public void execute (String arg) {
                    // perform operation here
            }
    }

     이 방법은 Java에서 enum 값들은 오직 한번만 설정되는 것이 보장되는 특성을 사용한 것이다.

    해당 enum 값이 전역적으로 접근이 되는 순간부터, 해당 값은 singleton이고 class-loader를 통해서 lazy init 된다.

    하지만, enum type은 type에 관하여 inflexible하기 때문에 사용에 유의해야 한다.

     

    Final Wrapper를 이용하는 방법 (update soon)

    DCL과 Lazy initialization 이야기 (update soon)

     

    댓글

code by jaguarcode.