Whiteship's Note


EJ2E Item 3. 싱글톤 속성은 private 성성자 또는 enum 타입으로

Java : 2008.10.28 09:41


싱글톤은 객체를 딱 한번만 생성하는 클래스.

1.5 전에 싱글톤을 구현하는 방법은 두 개였다. 둘 다 private 생성자와 public static 멤버를 노출한다.

첫 번째 방법: public static filed

public class Elvis {
  public static final Elvis INSTANCE = new Elvis();
  private Elvis(){...}
  ...
}

private 생성자는 딱 한 번 static final 필드인 Elvis.INSTANCE를 초기화할 때만 호출한다. 한 가지 주의 할 것이 있는데...
a privileged client can invoke the private constructor reflectively
(Item 53) with the aid of the AccessibleObject.setAccessible method
이 부분 잘 모르겠슴.

두 번째 방법: public static factory method

public class Elvis {
  private static final Elvis INSTANCE = new Elvis();
  private Elvis(){...}
  public static Elvis getInstance() { return INSTANCE; }
  ...
}

위에서 잘 모르는 부분의 문제가 이렇게 하면 발생하지 않아. (OSAF도 이런식으로 싱글톤을 만들었쥐.) 좀 더 유연하게 변경할 수 있다. Generic 타입을 고려할 수 있다.(Item 27) 이것도 모르겠군.

위의 두 가지 방법은 Serializable 하지 못한다. (static 필드는 직렬화 안 되니깐) 따라서 모든 객체 필드를 transient로 선언하거나 readResolve 메소드(Item 77)를 제공해야 한다.

세 번째 방법: Enum singleton

1.5 부터 사용할 수 있는 싱글톤 구현 방법. 캬.. 드디어 새로운 방법 등장이구나.

public enum Elvis {
  INSTANCE;
  ...
}

이 방법은 기능적으로는 public filed 접근 방법과 동일하지만, 간결하고, 여러 객체를 생성할 여지도 없으며, 직렬화도 제공한다는 점에서 차이가 있다.
a single-element enum type is the best way to implement a singleton
저자가 이 정도로 권장할 정도니까.. 사용해 봐야겠다.
top


Singletons and lazy loading

Design Pattern : 2007.01.27 10:40


참조 : http://www.oreillynet.com/onjava/blog/2007/01/singletons_and_lazy_loading.html?CMP=OTC-FP2116136014&ATT=Singletons+and+lazy+loading

싱글톤 패턴이 클래스 달랑 하나여서 제일 간단해 보이는 디자인 패턴임에도 불구하고 Lazy Loading 또는 초기화 지연 기법을 사용하려고 하면 굉장히 복잡해 지네요. 그리고 동기화 문제도 생각하면 syncronized가 필수가 됩니다. 그래서 보통은 아래 처럼 구현을 하죠.

class Singleton {

    private static Singleton instance;

    private Singleton() {
    }

    public static synchronized Singleton getInstance() {
        return (instance != null) ? instance : (instance = new Singleton())
    }
}

하니면 초기화 지연 기법을 포기하고 아예 처음 변수를 선언할 때 객체도 만들어 버립니다.

class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton() {
    }

    public static synchronized Singleton getInstance() {
        return instance;
    }
}

아니면 Head First Design Pattern에도 살짝 나왔던 것 같은데 2중 체크 라는 것을 합니다. 하지만 이것도 되는 것 처럼 보일 뿐 제대로 동작하지 않습니다.

// Correct Double-Checked Locking for 32-bit primitives
class Foo {
    private int cachedHashCode = 0;
    public int hashCode() {
        int h = cachedHashCode;
        if (h == 0)
        synchronized(this) {
            if (cachedHashCode != 0) return cachedHashCode;
            h = computeHashCode();
            cachedHashCode = h;
        }
        return h;
    }
    // other functions and members...
}

예제 코드가 좀 거시기 하지만 얼추 이와 비슷한 코드들이 동기화 관련된 부분에서 자주 보입니다. 이게 제대로 동작하지 않는 이유는 빨간 색 코드를 호출해서 h가 참조할 객체가 만들어 지는 도중에 녹색 조건 문에 다른 쓰레드가 걸려서 다 만들어 지지도 않은 객체의 레퍼런스를 가져갈 수 있기 때문인 것 같은데 이 것에 대한 자세한 내용은 여기를 참조하세요.

Anyway... 그럼 어떻게 하면 좀더 간단하고 빠르게(syncronized를 쓰지 않고) 싱글톤과 초기화 지연 기법을 사용할 수 있을까 하는게 참조한 글의 주제 였는데요.

public class Singleton {

    static class SingletonHolder {
        static Singleton instance = new Singleton();
    }

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

}

이 코드는 위키피디아에 Initialization on Demand Holder를 참조 했다고 합니다. 위 코드를 보시면 먼저 Syncronized가 사라진걸 볼 수 있구요. 따라서 멀티 쓰레드 환경에서 매우 빠르게 동작할 것 같습니다. 그리고 초기화 지연은 어떻게 보장을 할까요. getInstance() 메소드가 호출되면 그 때 static inner 클래스인 SingletonHolder가 JVM에 로딩 되게 되고 그 때서야 객체는 만들어 지게 되는거죠. 그리고 그 이후에는 static이니까 싱글톤.. 단일 객체를 보장하게 되는 겁니다.

'Design Pattern' 카테고리의 다른 글

데코레이터(Decorator) 패턴  (0) 2008.10.01
프록시(Proxy) 패턴  (0) 2008.09.26
JUnit 공부하자.  (0) 2008.09.01
H.F.OOAD 5장 문제  (0) 2007.11.20
상위 클래스 보다는 인터페이스를...  (4) 2007.08.31
Singletons and lazy loading  (2) 2007.01.27
객체지향 디자인 원칙  (2) 2006.12.11
Losely Coupled를 활용하라.  (0) 2006.12.11
상속보다는 구성을 활용한다.  (0) 2006.12.11
바뀌는 부분을 캡슐화 한다.  (0) 2006.12.11
State Pattern 예제  (2) 2006.12.10
top