싱글턴 패턴은 해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록 하기 위한 패턴입니다.
객체가 하나만 있으면 되는 것들이 무수히 많다.
그렇다면 왜 하나만 있으면 될까?
만약 인스턴스가 두 개 이상 만들면 프로그램이 이상하게 돌아간다든가 불필요한 리소스 자원을 소모 하게된다.
위에 객체로 하나 예시를 들면
DB 커넥션 풀은 하나만 있으면 된다. 여러 인스턴스를 만들경우 풀의 의미도 사라지고 불필요한 자원만 소모한다.
여러 객체에서 싱글턴 객체에 접근이 가능하다.
이는 객체를 무상태성으로 만들던가 불변 객체로 생성 해야한다. 상태에 대해서는 읽기만 할 수 있도록 만들어야한다.
만약 상태(데이터)를 변경한다면 여러 객체에서 상호작용하는 과정에서 오작동할 가능성이 있다.
public class Singleton {
public static int Data = 0;
public static Connection Connection = new Connection();
}
컴파일 시점에 Singleton의 전역 변수들이 메모리에 올라간다.
근데 만약 저 변수들을 사용하지 않는다면? 굳이 불필요한 메모리만 사용하게된다.
public class Singleton {
private static Singleton instance = null;
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
위 코드가 가장 간단하고 고전적인 싱글톤 객체 생성법이다.
그런데... 여러가지 문제점이 있다.
만약 멀티스레드 환경에서 생성을 한다면?
public class Singleton {
private static Singleton instance = null;
public static Singleton getInstance() {
if(instance == null) {
// 문제 발생 영역 (임계 영역)
instance = new Singleton();
}
return instance;
}
}
문제 발생 시나리오
싱글턴이 무엇인가?
하나의 객체를 생성하는게 목적인데 객체가 두 개 생성되었다.
이를 해결하는 방법이 몇 가지 있다.
1. static 인스턴스 변수를 바로 객체를 바로 생성하기
2. synchronized 키워드를 사용하기
3. inner class를 사용해서 싱글턴 생성하기 (LazyHolder 방식)
4. Enum 방식
5. DCL(Double Checked Locking) 방식
public static Singleton getInstance() {
if(instance == null) { // one check
synchronized (Singleton.class) { // 먼저 스레드가 들어오면 다른 스레드는 여기서 대기한다.
if(instance == null) { // two check
instance = new SingletonV4();
}
}
}
return instance;
}