사용의도
- 단 하나의 인스턴스만이 필요하고, 이를 접근하는 경우 사용합니다.
생성방식
1. Eager Initialization
- 바이트코드(.class파일) -> native code(JVM이 실행할 수 있는 코드) 로 변환하는 과정에서
- ClassLoader 가 클래스를 로딩 할 때, Initializing 과정에서 static 변수가 할당되고, static 블록이 수행되는 것을 이용하여 구현합니다. 참고
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
코드 | 설명 |
private static final Singleton INSTANCE = new Singleton(); | Initializing 과정에서 한번만 수행되므로, 이때 인스턴스를 생성합니다. |
private Singleton(){} | 외부에서 Singleton 객체를 생성할 수 없도록 막습니다. |
public Singleton getInstance() | 한번만 생성한 인스턴스를 외부에서 가져다가 사용할 수 있도록 합니다. |
2. Lazy Initialization
- 싱글톤 인스턴스를 생성하는 과정에서 여러 쓰레드에서 중복되어 한 시점에 인스턴스를 생성하는 것을 항상 조심해야합니다.
- 따라서 아래와 같은 방식으로 생성합니다.
2.1. Lazy Initialization with synchronized block
- 자바에서 mutex 접근을 막기위해 사용되는 synchronized keyword 를 사용하여 멀티쓰레드환경에서 해당 블록에 하나의 쓰레드만 접근할 수 있도록 제한하는 방식으로 구현합니다.
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {}
public static synchronized Singleton getInstance() {
if(INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
단점
- 인스턴스를 생성한 이후에도 getInstance() 메소드를 호출할 때마다 하나의 쓰레드만 접근할 수 있으므로 성능에 좋지않습니다.
2.2. Lazy Initialization, Double checking locking
- null 이 아닌경우에는 synchronized 블록을 거치지 않고 바로 리턴하기위해 사용됩니다.
public class Singleton {
private volatile static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if(INSTANCE == null) {
synchronized (Singleton.class) {
if(INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
volatile keyword
- Java 변수를 Main Memory 에 저장하겠다는 것을 명시하는 것입니다.
- 매번 변수의 값을 Read 할 때마다 CPU Cache 에 저장된 값이 아닌 Main Memory 에서 읽습니다.
- 변수의 값을 Write 할 때마다 Main Memory 에 까지 작성하는 것입니다.
- 마치 Cache 쓰기 정책의 Write Through 같은 느낌입니다.
2.3. Lazy Initialization, Enum
- Enum 은
- static constant 입니다.
- 하지만 compile time constant 는 아닙니다.
- Enum 클래스는 처음 사용하는 시점에 ClassLoader 에 의해서 로딩됩니다.
- 그 이후에는 더이상 사용해도 로딩이 되지 않으므로, Thread-safe 합니다.
public enum Singleton {
INSTANCE;
}
2.4. Lazy Initialization, Lazy Holder
- static 변수나 블럭은 JVM에 의해서 ClassLoader 가 Initialization 과정에서 수행하거나 할당하지만
- static InnerClass 의 경우에는 Enum 과 마찬가지로 처음 사용하는 시점에 한번만 ClassLoader 에 의해서 로딩됩니다.
- 해당 원리를 이용해서 Eager Initialization 과는 다르게 사용시점에 한번만 로딩하도록 구현합니다.
public class Singleton {
private Singleton() {}
private static class InnerClass {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() { // <-- 해당 메소드가 처음 수행될 때 ClassLoader 에 의해서 로딩됩니다.
return InnerClass.INSTANCE;
}
}
참고
'디자인패턴' 카테고리의 다른 글
브릿지 패턴 (Bridge Pattern) (0) | 2021.06.18 |
---|---|
어댑터 패턴 (Adapter pattern) (0) | 2021.06.12 |
원형패턴 (Prototype Pattern) (0) | 2021.06.03 |
팩토리 메서드 패턴 (Factory Method Pattern) (0) | 2021.05.29 |
추상팩토리 패턴 (Abstract Factory Pattern) (0) | 2021.05.22 |