싱글톤이란?
•
threadPool, connectionPool과 같은 하나만 있어도 충분하고 여러개일때 이슈가 발생할 수 있는 공유 의존성 객체들을 관리하기 위한 패턴이다.
public class Singleton {
private static Singletone singleton;
private Singletone() {};
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
Java
복사
•
생성자를 외부에서 호출할 수 없도록 만들어두고 획득만 가능하게 했다.
•
위와 같은 방법을 lazy instantiation이라고 부른다.
•
위의 방법은 thread-safe하지 않다.
◦
Simple Singleton을 사용했을때, 객체의 유일성을 보장하고 싶으나 멀티스레드 환경에서는 생성 부분을 여러 스레드가 접근 가능하므로 스레드가 여러개 생성될 가능성이 있다.
•
멀티스레드 환경에서 싱글톤을 사용하는 방법들은 여러가지가 있는데 다음과 같다.
◦
결국 객체 생성 범위에서 이슈가 발생하므로 이 부분을 어떻게 처리하느냐에 따라 차이가 생긴다.
1.
동기화 키워드를 사용하기
•
synchronized 키워드를 사용해서 객체 생성 부분에 대한 스레드의 접근을 제한한다.
2.
인스턴스 미리 선언하기
•
static으로 선언해두었기 때문에 애플리케이션과 생명주기를 같이한다.
•
따라서 객체가 선언될 때, 생성자를 선언해두고 getInstance 메소드에서 객체 생성 부분을 제거한다.
3.
Double-Checked Locking
public class Singleton{
private static Singleton singleton;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(singleton == null){
synchronized (Singlton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton
}
}
Java
복사
•
단순히 synchronized로 감싸는 경우, 초기화되어있는지 확인하기 위해 synchronized 블록에 접근해야 한다.
•
이 작업은 매우 비싼 작업이므로 오버헤드를 줄이기 위해, 이중으로 체크하게끔 로직을 작성하였다.
•
동기화 블록 안에 다시 if문을 두어 커넥션풀을 초기화하는 동안 다른 스레드가 getConnection()를 호출하면 초기화되지 않아 동기화 블록으로 진입하기 위해 대기하게 된다.
•
초기화 작업이 끝나고 대기하던 해당 작업이 동기화 블록에 접근하는 경우, 동기화 블록 내부 if 문에 의해서 초기화하지 않고 빠져나오게 된다.
•
이렇게 동기화 블록 전후로 초기화 여부를 검사하는 것을 Double-checked locking이라고 한다.
•
다만 이는 컴파일 단계에서 예상치 않은 이슈가 발생할 수 있다.
◦
singleton = new Singleton();
◦
이 코드는 일반적으로 다음과 같은 순서로 처리될 것을 기대하는데
1.
ConnectionPool 인스턴스 생성
2.
this.pool 변수에 ConnectionPool 참조값 대입
◦
컴파일러 최적화에 따라 코드가 재배치되는 경우, 참조값 대입이 먼저 발생하는 경우가 있다.
◦
따라서 인스턴스가 완전히 생성되지 않은 시점에 초기화되므로 이 시점에 다른 스레드가 접근하게 되는 경우, 비정상적인 객체를 획득하게 된다.
•
이는 volatile 키워드를 이용해서 해결할 수 있다.
◦
자바 5버전부터 volatile 키워드가 붙은 변수들은 컴파일 시에 재배치를 하지 않도록 했기 때문이다.
•
이를 적용하면 다음과 같은 코드가 된다.
public class Singleton{
private volatile static Singleton singleton;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(singleton == null){
synchronized (Singlton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton
}
}
Java
복사