객체지향의 디자인 패턴 중에서 아주 많이 들어봄 직하고, 비교적 간단한 패턴인 Singletone에 대해서 알아보겠습니다.
Single 이라는 말 그대로, 하나의 인스턴스를 가지는 패턴을 의미합니다.
디자인 패턴은 결국 어떠한 상황/문제를 해결하고 유지보수 하는데 효율적인 패턴의 한 형태입니다.
싱글톤은 언제, 왜 필요한가?

위 그림에서 처럼, 3명의 사람이 동시에 카메라를 사용할 수 없고, 1명씩만 사용해야하는 경우를 생각해봅시다.
실제 컴퓨팅 환경에서도 특정 앱(프로세스)이 PC Cam을 사용하고 있을때 다른 앱은 접근이 제하는 되는것이 일반적입니다.
그 외에도 생각보다 많습니다.
DB Connection pool의 경우 프로그램에서 여러개의 Connection을 만들고 프로세스는 1개의 연결을 관리 합니다.
더 예를 든다면
- 로그 관리 ( Logging ) : 어플리케이션 전반에 걸친 로그를 관리하는 인스턴스는 일반적으로 싱글톤으로 관리합니다.
- 구성관리 ( Configuration ) : 어플리케이션 설정관리의 경우도 중앙에서 관리하도록 하려고 싱글톤을 사용합니다.
- 세션 관리 ( Session ) : 세션 관리하는 매니저는 중복 생성을 피해야하므로, 싱글 톤으로 구현합니다.
- 하드드웨어 인터페이스 엑세스 ( Hardware Interface Access ) : 하드웨어의 경우 중복접근을 방지해야 합니다.
싱글톤 패턴 구현 방법
- 기본 생성자를 private 으로 생성하여, 외부에서 직접 생성을 방지합니다.
- 사용자에게 생성된 인스턴스를 제공할 static 메소드가 있습니다.
기본 프로그램 예제
package Singleton;
public class Singleton {
// 유일한 인스턴스를 저장할 변수
private static Singleton instance;
// 생성자를 private으로 선언하여 외부에서의 인스턴스화를 방지
private Singleton() {}
// 인스턴스에 접근할 수 있는 글로벌 접근 지점
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
위 코드는 Thread-Safe 문제가 있습니다.
만약 프로그램이 멀티 스레드로 동작하게 되면, getInstance( )를 했을때 if (instance == null)에서 동시에 null을 인식해서 2개의 인스턴스가 생성 될 수 있습니다.
이런 경우 어떻게 할까요? 바로 Java에서 synchronized 키워드를 사용합니다.
Synchronized 활용
package Singleton;
public class Singleton {
private static Singleton instance;
private Singleton() {}
// synchronized로 멀티 스레드 환경의 동시성 보장
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
이제 멀티 스레드에서 문제는 해결되었습니다.
하지만, synchronized 키워드는 선능이 저하됩니다. 그 전보다 대략 100배 정도 느려진다고 합니다.
그렇다면 성능문제를 해결하면서 멀티 스레드 문제를 해결 할수 없을까요? 이중 잠금 체크 ( double-checked locking ) 패턴을 활용합니다.
double-checked Locking 활용
package Singleton;
public class Singleton {
// 다중스레드 환경에서 인스턴스 초기화 보장
private static volatile Singleton instance;
private Singleton() {}
// 이중 잠금 체크 (
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
인스턴스가 생성되어 있는지 체크하고, 생성이 안되었으면 동기화를 수행합니다.
'volatile' 키워드는 빈번하게 변경되고, 그 변경이 다른 스레드에 즉각으로 변경합니다.