Notice
suyeonme
[디자인 패턴] 싱글톤 패턴(Singleton Pattern) 본문
헤드 퍼스트 디자인 패턴을 읽고 정리한 내용입니다.
1. 싱글톤 패턴(Singleton Pattern)이란?
- 클래스 인스턴스를 하나만 만들고, 그 인스턴스로의 전역 접근을 제공하는 디자인 패턴이다.
- 스레드 풀, 캐시, 대화상자, 사용자 설정, 레지스트리 설정을 처리하는 객체, 로그 기록용 객체, 디바이스 드라이버등에 주로 사용된다.
1.1 전역 변수 vs 싱글톤
- 전역 변수에 객체를 대입하면, 애플리케이션이 시작될 때 객체가 생성된다. 만약 객체가 자원을 많이 사용할 경우, 그리고 해당 객체가 사용되지 않으면 불필요하게 자원을 낭비할 수 있다.
2. 멀티스레딩을 고려하지 않은 싱글톤
- 간단하지만, 멀티스레드 환경에서는 문제가 발생할 수 있다.
- 생성자를 private으로 선언했으므로 Singleton에서만 클래스의 인스턴스를 생성할 수 있다.
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
3. 멀티스레딩을 고려한 싱글톤(동기화)
동기화(synchronize)란?
여러 개의 스레드가 한 개의 자원을 사용하고자 할 때 해당 스레드만 제외하고 나머지는 접근을 못하도록 막는 것이다.
동기화(synchronize)가 필요한 경우
- 하나의 객체를 여러 스레드에서 동시에 사용할 경우
- static으로 선언한 객체를 여러 스레드에서 동시에 사용할 경우
3.1 synchronized 사용
- synchronized 키워드를 사용하면 한 스레드가 메소드 사용을 끝내기전까지 다른 스레드는 기다려야한다.
- synchronized는 성능 저하를 발생시킨다는 단점이 있다.
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
3.2 인스턴스를 만들어서 반환
- 클래스가 로딩될 때 JVM에서 Singleton의 하나뿐인 인스턴스를 생성한다.
- JVM에서 인스턴스를 생성하기 전까지 어떤 스레드도 uniqueInstance 정적 변수에 접근할 수 없다.
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return uniqueInstance;
}
}
3.3 DCL(Double-Checked Locking) 사용
volatile 키워드
- CPU chach 메모리가 아닌 메인 메모리(RAM)에 값을 read하고 write한다. 따라서 다른 스레드라도 같은 메모리 주소를 참조한다.
- 멀티스레드 환경에서 하나의 스레드만 read, write하고, 다른 스레드는 onlyread가 보장되는 경우 사용한다. 즉 여러개의 스레드가 write하는 상황이라면 race condition을 해결할 수 없다.
- volatile로 선언된 변수가 있는 코드는 최적화되지 않는다.
volatile키워드를 사용하여 싱글톤 구현
- 인스턴스가 생성되어 있는지 확인 후, 생성되어 있지 않을 때만 동기화할 수 있다.
- volatile 키워드 사용시, 멀티스레딩 환경에서도 uniqueInstance 변수가 Singleton 인스턴스로 초기화되는 과정이 올바르게 진행된다.
- DCL은 자바 5 이상 버전에서 사용할 수 있다.
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if(uniqueInstance == null) {
synchronized (Singleton.class) {
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
}
}
3.4 enum 사용
직렬화, 역직렬화 (Serialization, Deserialization)란?
- 자바 시스템 내부에서 사용되는 객체 또는 데이터를 외부의 자바 시스템에서도 사용할 수 있도록 바이트(byte) 형태로 데이터 변환하는 기술(직렬화)과 바이트로 변환된 데이터를 다시 객체로 변환하는 기술(역직렬화)
- 클래스를 역직렬화할 때 새로운 인스턴스가 생성되어 싱글톤 속성을 위반한다. (직렬화할 때 readObject()를 구현해야하는데, readObject()는 매번 새로운 인스턴스를 반환한다.)
Reflection이란?
- 구체적인 클래스 타입을 몰라도 해당 클래스의 메서드, 타입, 변수등에 접근할 수 있도록 해주는 자바 API이다. (컴파일타임이 아닌 런타임에 동적으로 특정한 클래스의 정보를 추출할 수 있도록 하는 프로그래밍 기법)
- 리플렉션을 이용하면 런타임에 private 생성자에 접근하여 새로운 인스턴스를 생성할 수 있게 되어 싱글톤 속성을 위반한다.
enum을 사용하여 싱글톤 구현
- enum을 사용하여 싱글톤을 구현하면, 동기화 문제, 클래스 로딩 문제, 리플렉션, 직렬화/역직렬화 문제를 해결할 수 있다.
- enum은 기본적으로 직렬화가 가능하므로 Serializable 인터페이스를 구현할 필요가 없다.
- 생성자가 private으로 제한되므로 외부에서의 인스턴스 생성을 제한한다.
- 인스턴스가 JVM내에 하나만 존재한다는 것이 100% 보장된다. 따라서 가장 추천되는 방법이다.
public enum Singleton {
UNIQUE_INSTANCE;
}
public class SingletonClient {
public static void main(String[] args) {
Singleton singleton = Singleton.UNIQUE_INSTANCE;
}
}
'프로그래밍👩🏻💻 > 디자인 패턴' 카테고리의 다른 글
[디자인 패턴] 어댑터 패턴(Adapter Pattern), 퍼사드 패턴(Facade Pattern) (0) | 2022.07.11 |
---|---|
[디자인 패턴] 커맨드패턴(Command Pattern) (0) | 2022.07.10 |
[디자인 패턴] 팩토리 패턴(Factory Pattern) (0) | 2022.06.26 |
[디자인 패턴] 데코레이터 패턴(Decorator Patter) (0) | 2022.06.19 |
[디자인 패턴] 옵저버 패턴(Observer Pattern) (0) | 2022.06.18 |
Comments