728x90
정의 및 특징
- 객체의 인스턴스가 오직 1개로만 만들어져서 공유하는 패턴
- 하나의 인스턴스를 메모리에 등록해서 여러 스레드가 동시에 해당 인스턴스를 공유하여 사용하게끔 할 수 있어서, 요청이 많은 곳에서 사용하면 효율을 높임 (전역으로 사용되는 인스턴스이기 때문)
- 싱글턴을 만들때 동시성(Concurrency) 문제를 고려해야함
- 내부 상태를 변경하기가 어려움 (상태 넣기가 어려움)
싱글턴 패턴 구현
싱글턴패턴 기본구현은 static영역에 객체 instance를 미리 하나 올려서 getInstance() 메서드를 통해서만 조회할 수 있게 구현을 하며, 생성자를 private으로 막아 new 키워드로 객체인스턴스를 생성못하게 막음, 그외 구현 방식은 여러가지가 있음
싱글턴 패턴 구현 방법
- Eager Initialization
- Synchronized
- Double Checking Locking
- Enum
- LazyHolder
Eager Initialization
public class Singleton {
private static Singleton INSTACNE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTACNE;
}
}
- static 키워드의 특징을 이용해서 클래스 로더가 초기화 하는 시점에서 정적 바인딩(컴파일 시점에서 성격이 결정됨)을 통해 해당 인스턴스를 메모리에 등록해서 사용
- 멀티쓰레드 환경에서 쓰레드 세이프가 보장이 되지 않음 (다음 단계에서 Synchronized 패턴이 추가된 이유)
Synchronized
public class Singleton {
private static Singleton INSTACNE = new Singleton();
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (INSTACNE == null) {
INSTACNE = new Singleton();
}
return INSTACNE;
}
}
- synchronized 키워드를 이용하여 초기화한 방식, 동기화를 지정하여 처리함
- 인스턴스가 필요한 시점에 요청 하여 동적 바인딩(런타임시에 성격이 결정됨)을 통해 인스턴스를 생성하는 방식
- 동기화 블록을 매번 걷혀야하기 때문에 성능 이슈가 발생함 (synchronized시 속도가 100배 정도 감소한다고함)
- 성능 이슈로 인해서 Double Check Locking 방식이 생김
Double Check Locking
public class Singleton {
private volatile static Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
- instance가 생성되지 않은 경우에만 동기화 처리를 하는 방식
- 오브젝트 재배치 문제로 인하여 volatile 키워드를 선언해야함
Enum
public enum Singleton {
INSTANCE;
}
- Enum 자체가 instance가 쓰레드 세이프함
- 구현이 매우 간단함
- Enum내에 다른 메소드가 있는 경우 해당 메소드가 쓰레드 세이프 관련해서 처리해야함
Lazy Holder
public class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
return LazyHolder.INSTANCE;
}
public static class LazyHolder {
private static Singleton INSTANCE = new Singleton();
}
}
- 가장 완벽한 싱글턴 구현 방법
- static영역에 초기화를 하지만 객체가 필요한시점까지 초기화를 미루는 방식
- 클래스의 getInstance() 메서드에서 LazyHolder.INSTANCE를 참조하는 순간 클래스가 로딩되며 초기화가 진행된다. Class를 로딩하고 초기화하는 시점은 스레드세이프를 보장하기 때문에 volatile이나 synchronized 같은 키워드가 없어도 스레드세이프하며 성능이슈가 없음
싱글턴 패턴 문제점
- 구현하는 코드 자체가 많이 들어감
- 의존관계상 클라이언트가 구체 클래스에 의존 (DIP 위반)
- 구체 클래스에 의존해서 OCP 원칙을 위반할 가능성이 높음
- private 생성자로 자식클래스 만들기 어려움, 유연성이 떨어짐
'JAVA' 카테고리의 다른 글
[JAVA] Map / getOrDefault (0) | 2021.06.10 |
---|---|
[JAVA] 예외(Exception) (0) | 2021.06.07 |
[JAVA] JVM 구조 (0) | 2021.06.03 |
[JAVA] Enum (0) | 2021.06.02 |
[JAVA] GC의 종류 (0) | 2021.05.23 |