<aside> 💡 singleton 이란 인스턴스를 오직 하나만 생성할 수 있는 클래스
</aside>
리소스를 많이 차지하는 역할
을 하는 무거운 클래스일 때
한번만 미리 만들어두는, 가장 직관적이면서도 심플
static final 이라 멀티 스레드 환경에서도 안전
그러나 static 멤버는 당장 객체를 사용하지 않더라도 메모리에 적재하기 때문에 만일 리소스가 큰 객체일 경우, 공간 자원 낭비
가 발생
예외 처리 불가
만일 그리 크지 않은 객체라면 이 기법으로 적용해도 무리가 없음
class Singleton {
// 싱글톤 클래스 객체를 담을 인스턴스 변수
private static final Singleton INSTANCE = new Singleton();
// 생성자를 private로 선언 (외부에서 new 사용 X)
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
Reflection 방어하기
AccessibleObject.setAccessible
을 사용해 private 생성자를 호출할 수 있는 문제점이 있음public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() {
if( INSTANCE != null) {
throw new RuntimeException("Can't create Constructor");
}
//...
}
}
Singleton Class 직/역직렬화 시 방어하기
Serializable
을 구현하는 것만으로는 부족transient
(일시적) 약어를 선언하고 readResolve
메서드를 제공해야한다. (item 89) 이렇게 하지 않으면, 역직렬화(deserialize)시 새로운 인스턴스가 생성됨public class Elvis implements Serializable{
private static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public static Elvis getInstance() { return INSTANCE; }
// singleton임을 보장
private Object readResolve() {
// 역직렬화가 되어 새로운 인스턴스가 생성되더라도 INSTANCE를 반환하여 싱글턴 보장
// 새로운 인스턴스는 GC에 의해 UnReachable 형태로 판별되어 제거
return INSTANCE;
}
}
static block 을 이용해 예외를 잡을 수 있음
<aside> 💡 static block 클래스가 로딩되고 클래스 변수가 준비된 후 자동으로 실행되는 블록
</aside>
그러나 여전히 static 의 특성으로 사용하지 않는데도 공간을 차지함
class Singleton {
// 싱글톤 클래스 객체를 담을 인스턴스 변수
private static Singleton instance;
// 생성자를 private로 선언 (외부에서 new 사용 X)
private Singleton() {}
// static 블록을 이용해 예외 처리
static {
try {
instance = new Singleton();
} catch (Exception e) {
throw new RuntimeException("싱글톤 객체 생성 오류");
}
}
public static Singleton getInstance() {
return instance;
}
}
객체 생성에 대한 관리를 내부적으로 처리
메서드를 호출했을 때 인스턴스 변수의 null 유무에 따라 초기화 하거나 있는 걸 반환하는 기법
위의 미사용 고정 메모리 차지의 한계점 극복
그러나 thread safe 하지 않는 치명적인 단점을 가지고 있음
class Singleton {
// 싱글톤 클래스 객체를 담을 인스턴스 변수
private static Singleton instance;
// 생성자를 private로 선언 (외부에서 new 사용 X)
private Singleton() {}
// 외부에서 정적 메서드를 호출하면 그제서야 초기화 진행 (lazy)
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton(); // 오직 1개의 객체만 생성
}
return instance;
}
}
각 스레드는 자신의 실행단위를 기억하면서 코드를 위에서 아래로 읽어감
문제 발생 시나리오
Test Code