싱글톤은 디자인 패턴중 하나다
해당 클래스에 해당하는 객체를 하나만 생성하는 디자인 패턴이다.
public class SingletonClass() {
// private static으로 singletonInstance를 생성하였다.
private static SingletonClass singletonInstance = new SingletonClass();
// 생성자를 private으로 생성해 다른 클래스에서 호출할 수 없다.
private SingletonClass(){}
// getInstance 메서드를 통해 singletoneInstance를 호출
public getInstance(){
return singletoneInstance;
}
}
직관적으로 가장 쉽게 싱글턴 객체를 생성하는 방법이다.
우선 문제를 추적할 수 있도록 변경해보자.
public class SingletonClass() {
// private static으로 singletonInstance를 생성하였다.
private static SingletonClass singletonInstance;
// 생성자를 private으로 생성해 다른 클래스에서 호출할 수 없다.
private SingletonClass(){}
// static block 안에 singletonInstacne 가져올 수 있게 변경
// 예외를 던질 수 있게 변경
static{
try{
singletoneInstance = new SingletonClass();
}catch(exception e){
throw new RuntimeException("Exception occured in creating singleton instance");
}
}
// getInstance() 메소드를 통해 singletonInstance를 호출
pulbic getInstance() {
return singletonInstance;
}
}
문제를 추적할 수 있게 변경했지만
인스턴스를 원하는 시점에 생성할 수 있도록 변경해보자.
public class SingletonClass() {
// static으로 singletonInstance를 선언하였다.
static SingletonClass singletonInstance;
// 생성자를 private으로 생성해 다른 클래스에서 호출할 수 없다.
private SingletonClass(){}
// getInstance 메서드를 통해 singletonInstance가 가리키는 값이 null인 경우에만 SingletonClass 객체 생성
public getInstance(){
if(singletoneInstance == null){
singletoneInstance = new SingletoneClass();
}
return singletoneInstance
}
}
이 방식으로 구현할 경우 1, 2번에서 안고 있던 메모리 낭비 문제에 대해 해결된다.
방법으로 구현을 해도 괜찮은 경우는 single-thread 환경이 보장된 경우다.
multi-thread 환경에서 동기화?
만약 인스턴스가 생성되지 않은 시점에서 여러 쓰레드가 동시에 getInstance()를 호출한다면 예상치 못한 결과를 얻을 수 있으며, 단 하나의 인스턴스를 생성한다는 싱글톤 패턴에 위반하는 문제점이 야기될 수 있다.
멀티 스레드 환경에서는 어떻게 싱글턴을 보장할 수 있을까?
해당 메서드가 실행 될 때 synchronized키워드를 통해서 막아버리면 다른 스레드들이 해당 메서드에 접근 할 수 없게 되고 그럼 하나의 인스턴스만을 가질 수 있다.
public class SingletonClass() {
// static으로 singletonInstance를 선언하였다.
static SingletonClass singletonInstance;
// 생성자를 private으로 생성해 다른 클래스에서 호출할 수 없다.
private SingletonClass(){}
// getInstance 메서드를 통해 singletonInstance가 가리키는 값이 null인 경우에만 SingletonClass 객체 생성
// synchronized 키워드를 통해 해당 메서드에 하나의 스레드만이 접근할 수 있다.
public synchronized getInstance(){
if(singletoneInstance == null){
singletoneInstance = new SingletoneClass();
}
return singletoneInstance
}
}
getInstance 메서드를 다음과 같이 바꿔서 문제를 해결 한다.
public static Singleton getInstance(){
if(singletonInstance == null){
synchronized (SingletonClass.class) {
if(singletonInstance == null){
instance = new SingletonClass();
}
}
}
return instance;
}