이끼의 생각

[Android Design Pattern] 싱글톤 패턴 (2) 본문

Mobile App/안드로이드와 Java

[Android Design Pattern] 싱글톤 패턴 (2)

IKKIson 2019. 5. 12. 06:31

Singleton Coding


싱글톤 패턴을 자바언어로 구현해 보겠습니다.




1. Lazy Initalization (The Classic Initialization)


늦은 초기화 방식으로 고전적인 방법의 싱글턴 패턴 방식입니다.


public class Singleton {

private static Singleton uniqueInstance;


// other useful instance variables here


private Singleton() {}


public static Singleton getInstance() {

if (uniqueInstance == null) {

uniqueInstance = new Singleton();

}

return uniqueInstance;

}

// other useful methods here

}

기본적인 싱글톤 방식 이 싱글톤의 인스턴스가 필요할 에 getInstance가 호출되어 인스턴스를 생성 후 반환하는 방식입니다.


이 방식의 문제점은 멀티 쓰레드 환경에서 여러 곳에서 getInstance 호출 시에 인스턴스가 두번 생성될 수 가 있어 동기화 문제가 발생합니다.




2. Thread Safe Lazy Initialization


늦은 초기화 방식에 Thread-safe를 위한 방식 입니다.


public class Singleton { private static Singleton uniqueInstance = new Singleton(); private Singleton() {} public static sychronized Singleton getInstance() { if(uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }


sychronized 키워드로 thread-safe하게 만든 방식 입니다. sychronized는 이 메소드를 사용한 쓰레드가 사용이 끝나기 전까지 다른 쓰레드가 이 메소드를 동시에 실행할 수 없게 합니다.


단점을 보안하였지만 또다른 문제점이 발생하는데, 자바에서 이러한 동기화를 사용하면 성능이 약 100배 정도 저하가 되어 속도가 중요한 경우 적합하지 않습니다. 일단 이 방법은 권장하는 방법도 아닙니다.




3. Double-Checked locking Thread Safe Lazy Initialization


2번에서 성능저하를 완하한 방식입니다.


public class Singleton {
    private static Singleton uniqueInstance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        if(uniqueInstance == null) {
            sychronized (Singleton.class) {
                if(uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
        }
        return uniqueInstance;
    }
}


getInstance 메서드에서 sychronized를 사용하지 않고 (1)[인스턴스 존재여부 확인] -> (2)[한번 더 확인하여 동기화].

인스턴스 확인 후 최초로 생성 시에만 sychronized로 동기화를 사용한다. 처음 생성 이후 synchonized 메서드 블럭이 되지 않고 Thread-safe하므로 성능저하는 일부 완화하였다.


이 방법 역시 권장하지 않는다.




4. Eager Initialization


이른 초기화 방식, 싱글톤 객체를 instance 네이밍하여 변수를 미리 생성하여 사용하는 방식입니다.


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

    private Singleton() {}

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


이 역시 기본적이고 권장하는 싱글톤 방식으로 생성자의 외부 호출을 막기위해 private 접근제어자를 사용합니다.

외부에서 접근가능한 메서드는 getInstance 메서드이며 생성자를 호출못하게 하여 유일한(final) 인스턴스만 반환하게 합니다.


 Lazy 방식에서 필요한 순간에 인스턴스를 생성하는 방식이였지만 이 방식은 어플리케이션이 인스턴스를 미리 생성한다. 

그리고 Lazy 방식에서의 Thread-safe 하지 않은 단점을 보안하는데 클래스 로더(Class Loader)에 의해 클래스가 최초 로딩 될때 JVM이 싱글통의 인스턴스를 생성하고 유일한 이 인스턴스를 생성하기 전에는 어떤 쓰레드도 static인 인스턴스 변수 uniqueInstance를 접근할 수 없어 Thread-safe 하게 됩니다. 즉, 멀티쓰레드에서 동시에 여러번 getInstance 호출로 발생한 유일한 인스턴스 생성을 보장할 수 있게 됩니다.


 앱을 시작하자마자 실행되는 배경음악이나 버튼을 누를 때 자주 사용하는 효과음과 같은 오디오 리소스와 같이 미리 생성하여 실행하여도 메모리 낭비가 되거나 부하에 문제가 되지 않은 경우 이 방식을 사용하면 좋습니다. 


 반대로 말하면 항상 싱글톤 객체가 메모리를 차지하고 있어 의도에 맞게 올바르게 사용하지 않으면 비효율적으로 메모리 낭비가 발생할 수있습니다.




5. Initialization on demand holder idiom


holer에 의한 초기화 방식입니다. 클래스 내에서 holder 클래스를 두어 JVM의 클래스 로더(Class Loader)와 클래스가 로드되는 시점을 이용합니다.


public class MyClass { private MyClass() { } private static class LazyHolder { public static final MyClass uniqueInstance = new MyClass(); } public static MyClass getInstance() { return LazyHolder.uniqueInstance; } }


클래스 안에 내부 클래스(Holder 클래스)에서 실제로 사용할 인스턴스를 생성하는 방법으로 Lazy방식의 장점과 Thread-safe를 보장하는 방식입니다.


가장 많이 사용되는 싱글턴방식입니다. 





6. Enum Initialization


Enum 초기화 방식으로 모든 Enum type을 소스코드 내에서 한번 초기화되는 방법을 이용합니다.


public enum Singleton {
    INSTANCE;
    pulic void excute (string arg) {
        //do something operation
    }
}

자세한 내용은 Joshua Bloch의 Effective Java 도서를 참고하는걸 추천합니다~~






참고 & 출처

http://bictoselfdev.blogspot.com/2018/05/singleton.html

https://dreamlog.tistory.com/495

'Mobile App > 안드로이드와 Java' 카테고리의 다른 글

[Android Design Pattern] 싱글톤 패턴 (1)  (0) 2019.05.12
Comments