////
Search
Duplicate
⌨️

Chatper 05. SingleTon

싱글톤이란?

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
복사