Singleton
instance를 만드는 절차를 추상화하는 패턴이다. Log 객체, ThreadPool 객체, WindowAdmin 객체 등 관리 역할을 수행하는 객체는 단 하나의 insance를 갖는 것이 바람직하다.
단 하나의 instance를 전역변수로 관리할 수도 있다. 하지만 클래스 정보에 유일한 인스턴스로 접근하는 방법을 자체적으로 관리하는 것이 더 좋다.
여러 구현 방법의 공통점
- private 생성자만을 정의해 외부 클래스에서 instacne 생성을 차단한다.
- 싱글톤을 구현하고자 하는 클래스 내부에 멤버 변수로 private static 변수를 생성한다.
- public static 메서드를 통해 외부에서 singleton instace에 접근할 수 있도록 한다.
방법 1 : Eager Initialization
- 해당 인스턴스를 사용하지 않을 때에도 인스턴스를 생성합니다.
- 따라서 File System, Database Connection과 같은 큰 리소스를 사용하는 경우에는 적합하지 않습니다. 이 경우 getInstance()를 호출하기 전까지 인스턴스를 생성하지 않는 것이 좋습니다.
- Exception handling을 제공하지 않습니다.
1
2
3
4
5
6
7
8
9
| public class Singleton{
private satic final Singleton instance = new Singleton();
pricate Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
|
방법 2 : Static Block Initialization
- 해당 인스턴스를 사용하지 않을 때에도 인스턴스를 생성합니다.
- 따라서 File System, Database Connection과 같은 큰 리소스를 사용하는 경우에는 적합하지 않습니다. 이 경우 getInstance()를 호출하기 전까지 인스턴스를 생성하지 않는 것이 좋습니다.
- 이전 방법에서 Exception handling만을 추가한 코드입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| public class Singleton{
private static Singleton instance;
private Singleton(){}
static{
try{
instance = new Singleton();
}catch(Exception e){
throw new RuntimeException("Exception occured in creating singleton instance");
}
}
public static Singleton getInstance(){
return instance;
}
}
|
방법 3 : Lazy Initialization
- getInstance()를 호출하기 전까지 인스턴스를 생성하지 않습니다.
- multi-thread 환경에서 동기화 문제가 발생합니다. 여러 개의 thread에서 여러 개의 instance를 생성할 수 있습니다.
- single-thread 환경에서는 사용할 수 있습니다.
- Exception handling을 제공하지 않습니다.
1
2
3
4
5
6
7
8
9
10
11
12
| public class Singleton{
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
|
방법 4 : Thread Safe Singleton
- 방법 3의 문제에서 synchronized 키워드를 사용해서 multi-thread 환경에서도 사용할 수 있습니다.
- 하지만 singleton instance 호출이 잦은 경우 앱의 성능이 떨어지는 문제가 발생합니다.
1
2
3
4
5
6
7
8
9
10
11
12
| public class Singleton{
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
|
방법 4-1. double checked locking
좀 더 개선된 방법은 아래와 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| public class Singleton{
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
|
방법 5 : Bill Pugh Singleton
- getInstance()를 호출할 때 SingletonHelper가 JVM 메모리에 로드되고 인스턴스를 생성합니다.
- synchronized를 사용하지 않아서 성능저하 없이 multi-thread 환경에서 사용할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| public class Singleton{
private Singleton(){}
// static inner class - inner classes are not loaded until they are referenced.
/**
* SingletonHolder is loaded on the first execution of
* Singleton.getInstance() or the first access to SingletonHolder.INSTANCE,
* not before.
*/
private static class SingeltonHelper{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonHelper.INSTANCE;
}
}
|
방법 5가 Thread-safe 한 이유
class initialization phase는 JLS(Java Language Specification)에 의해서 serial하게 진행됨이 보장됩니다. 따라서 multi-thread 상황에서도 synchronized 키워드가 필요하지 않습니다.
방법 5를 사용하는 경우
- initialization 과정의 비용이 높은 경우
- class-loading phase에 initializeing이 not safe한 경우 사용합니다.
Reference
Leave a comment