Singleton이란?
 프로그램상에서 동일한 인스턴스를 만들어 내는 것이 아닌 동일 인스턴스를 사용하게 하는 것
동일한 컨넥션 객체를 만든다던지, 하나만 사용되어야 하는 객체를 만들때 사용한다.
매우 자주 쓰이는 패턴중 하나이다.
Singleton Pattern들
Eager Initialization
가장 기본적인 Singleton Pattern.
먼저 클래스 내에 전역변수로 instance 변수를 생성하고 private static을 사용하여 인스턴스화에 상관없이 접근이 가능하면서 동시에 private 접근제한자를 사용하여 class.instance로 바로 접근 할 수 없도록 한다. 또 생성자에도 private 접근제한자를 붙여서 다른 클래스에서 new 방식의 새로운 인스턴스를 생성하는 것을 방지한다.
오로지 전역변수가 포함된 클래스 내에 getInstance() 같이 메서드를 만들어 사용하여 인스턴스에 접근하도록 한다.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | packageSingleTonExample;publicclassEagerInitialization {    // private static으로 전역변수 선언    privatestaticEagerInitialization instance = newEagerInitialization();        // private 생성자    privateEagerInitialization() {}        // 인스턴스 리턴 Method    publicstaticEagerInitialization getInstance() {        returninstance;    }} | 
static 전역변수로 생성되었으므로 클래스가 로딩될 때 객체가 생성되므로 Thread-Safe하다.
하지만 객체의 사용유무와 상관없이 클래스가 로딩되면 항상 객체가 생성되고, 메모리를 사용하므로 객체 생성비용이 적은 경우나 항상 쓰는 객체일 때 쓴다.
Lazy Initialization
 인스턴스 생성 시점이 인스턴스가 사용되는 시점이다. 따라서 객체 사용전까지는 메모리를 점유하지 않는다.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | packageSingleTonExample;publicclassLazyInitialization {    // private static으로 전역변수 선언     privatestaticLazyInitialization instance;    // private 생성자     privateLazyInitialization(){}        // 인스턴스 리턴 Method     // 인스턴스가 null일 때만 생성     publicstaticLazyInitialization getInstance(){        if(instance == null){            instance = newLazyInitialization();        }        returninstance;    }} | 
객체가 필요할 때 인스턴스를 얻을 수 있다. Eager Initialization 단점 보완.
하지만 multi-thread환경이라면 인스턴스가 두개 혹은 여러개 생성될 여지가 있다. Singleton 아니게 될 수 있다.
Thread sage Lazy Initialization
Lazy Initialization 단점을 보완하기위해 synchronized 를 사용한 방식.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | packageSingleTonExample;publicclassThreadSafeLazyInitialization{    // private static으로 전역변수 선언     privatestaticThreadSafeLazyInitializationinstance;    // private 생성자     privateThreadSafeLazyInitialization(){}        // 인스턴스 리턴 Method     // synchronized 사용     publicstaticsynchronizedThreadSafeLazyInitializationgetInstance(){        if(instance == null){            instance = newThreadSafeLazyInitialization();        }        returninstance;    }} | 
thread-safe하지만 synchronized를 쓰게 되면 내부적으로 multi-thread에 안전한 환경을 만들기 때문에 많은 비용이 발생한다. 따라서 성능저하가 일어날 수 있다.
Thread sage Lazy Initialization + Double-checked locking
위의 방식에서 성능저하를 방지하기위해 만든 방법.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | packageSingleTonExample;publicclassThreadSafeLazyInitialization {    // private static으로 전역변수 선언     privatestaticThreadSafeLazyInitialization instance;    // private 생성자     privateThreadSafeLazyInitialization(){}        // 인스턴스 리턴 Method     // instance생성을 두번 체크하여 성능저하 보완     publicstaticThreadSafeLazyInitialization getInstance(){                if(instance == null){            synchronized(ThreadSafeLazyInitialization.class) {                if(instance == null)                    instance = newThreadSafeLazyInitialization();            }        }        returninstance;    }} | 
Initialization on demand holder idiom
클래스 안에 Holder클래스를 만들어 Lazy initialization 방식을 가져가면서 thread 동기화 문제를 해결하는 방식이다. 중첩클래스인 Holder는 GetInstance Method가 호출되기 전까지는 참조되지 않으며 최초로 호출될 때 객체를 생성한다. static을 이용하여 한 번만 호출되게하고 final을 써서 다시 값이 할당되지 않게 한다.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | packageSingleTonExample;publicclassInitializationOnDemandHolderIdiom {    // private 생성자     privateInitializationOnDemandHolderIdiom(){}        // private static Holder 클래스     privatestaticclassSingleTonHolder{        privatestaticfinalInitializationOnDemandHolderIdiom instance = newInitializationOnDemandHolderIdiom();    }        // 인스턴스 리턴 Method     // Holder클래스에서 생성한 객체를 불러온다.     publicstaticInitializationOnDemandHolderIdiom getInstance(){        returnSingleTonHolder.instance;    }} | 
Singleton Pattern중 가장 많이 사용한다고 한다.
Initialization on demand holder idiom
이번에는 enum의 특징을 이용한 방법이다. enum type들은 프로그램 내에서 한 번 초기화되는 점을 이용함.
| 1 2 3 4 5 6 7 8 9 10 | packageSingleTonExample;publicenumEnumSingleTon {        INSTANCE;         static String test = "";         public static EnumInitialization getInstance()        {            test = "test";            return INSTANCE;        } | 
Enum이 생성될 때 multi thread로부터 안전하다. (추가된 Method들은 safed 하지 않을 수 있다.)
단 한번의 인스턴스 보장하고 사용이 간편하다.
Reference
http://limkydev.tistory.com/67
https://blog.seotory.com/post/2016/03/java-singleton-pattern
'프로그래밍 > 디자인 패턴(프로그래밍)' 카테고리의 다른 글
| [Design Pattern]Prototype Pattern이란? (0) | 2019.01.17 | 
|---|---|
| [Design Pattern]Builder Pattern이란? (0) | 2019.01.17 | 
| [Design Pattern]Abstract Factory Pattern이란? (0) | 2019.01.17 | 
| [Design Pattern]Factory Method Pattern이란? (0) | 2019.01.17 | 
| [프로그래밍]Design Pattern Outline, 디자인 패턴이란? (0) | 2019.01.16 | 
댓글