싱글턴 패턴은 특정 클래스에 객체 인스턴스가 하나만 만들어지도록 해 주는 패턴이다.
- 객체를 쓸 때 인스턴스가 2개 이상이면 프로그램이 이상하게 돌아가는 경우
- 자원을 불필요하게 사용하는 경우
- 결과에 일관성이 없어지는 경우
특정 상황에서는 객체가 1개만 있어도, 혹은 1개만 있어야 문제없이 수행되는 경우 활용하는 패턴이다.
- 스레드 풀
- 캐시
- 대화상자
- 사용자 설정
- 레지스트리 설정을 처리하는 객체
- 로그 기록용 객체
- 디바이스 드라이버
전역 변수에 객체를 대입하면 애플리케이션이 시작될 때 객체가 생성되는데 그 객체가 자원을 많이 차지하면서 사용하지 않는 경우에도 제어할 수 없다.
하지만 싱글턴 패턴을 활용하면 필요할 때만 객체를 만들 수 있다.
고전적인 싱글턴 패턴 구현법
생성자를 private
로 만들고 static
메소드를 통해 인스턴스를 얻도록 구현하기 때문에 생성자를 통해서 새로운 인스턴스를 얻을 수 있는 방법이 없다.
|
|
아직 인스턴스가 만들어지지 않았다면 private
으로 선언된 생성자를 사용하여 Singleton
객체를 만든 다음 uniqueInstance
에 그 객체를 대입한다.
이렇게 처리한다면 인스턴스가 필요한 상황이 닥치기 전까지 아예 인스턴스를 생성하지 않게 된다.
이러한 방법을 **게으른 인스턴스 생성(lazyinstantation)**이라고 부른다.
싱글턴 패턴의 정의
**싱글턴 패턴(Singleton Pattern)**은 클래스 인스턴스를 하나만 만드록, 그 인스턴스로의 전역 접근을 제공한다.
싱글턴 패턴을 실제로 정용할 때는 클래스에서 하나뿐인 인스턴스를 관리하도록 만들면 된다.
- 그리고 다른 어떤 클래스에서도 자신의 인스턴스를 추가로 만들지 못하게 해야한다.
어디서든 해당 인스턴스에 접근할 수 있도록 전역 접근 지점을 제공한다.
- 언제든 해당 인스턴스가 필요하면 클래스에 요청할 수 있게 만들어 놓고, 요청이 들어오면 하나뿐인 인스턴스를 건네주도록 한다.
- 자원을 많이 잡아먹는 인스턴스가 있다면 고전적인 싱글턴 처럼 게으른 방식으로 생성되도록 구현할 경우 유용할 수 있다.
classDiagram class Singleton { static uniqueInstance: Singleton static getInstance() Singleton }
싱글턴 패턴을 사용할 때는 일반적인 클래스를 만들 때와 마찬가지로 다양한 데이터와 메소드를 사용할 수 있다.
멀티 스레딩 문제
싱글턴 패턴을 사용할 때 인스턴스를 동시에 요청하는 상황에서 독립적인 인스턴스를 제공하지 못해 문제가 발생한다.
이러한 문제는 자바는 getInstance
에 synchronized
연산자를 추가하면, 메소드 사용 완료 후 요청하기 때문에 쉽게 해결이 가능하다.
|
|
더 효율적으로 멀티스레딩 문제 해결하기
synchronized
연산자를 getInstance
에 적용하는 방법은 동기화로 인해 성능 저하가 발생할 수 있고, 싱글턴 패턴에서 멀티스레딩 문제가 발생하는 시점은 메소드가 시작되는 때 뿐이다.
uniqueInstance
변수에 할당이 완료되면, 항상 새로운 인스턴스를 생성하지 않기 때문에 동기화 처리는 불필요한 오버헤드를 증가시킨다.
getInstance
메소드의 성능이 중요하지 않다면 큰 문제가 이닐 수 있지만 메소드를 동기화하면 성능이 100배 정도 저하된다. 따라서 병목이 된다면 별도의 처리가 필요하다.
인스턴스 처음부터 만들기
애플리케이션에서 Singleton
의 인스턴스를 생성하고 계속 사용하거나 인스턴스를 실행 중에 수시로 만들고 관리하기가 번거롭다면 아래와 같은 방식으로 처음부터 인스턴스를 만들 수 있다.
|
|
이 방법은 클래스가 로딩될 때 JVM에서 Singleton
의 하나뿐인 인스턴스를 생성해주며, JVM에서 하나뿐인 인스턴스를 생성하기 전까지 그 어떤 스레드도 uniqueInstance
정적 변수에 접근할 수 없다.
DCL을 통한 동기화 줄이기
**DCL(Double-Checked Locking)**을 사용하면 인스턴스가 생서오디어 있는지 확인한 다음 생성되어 있지 않았을 때만 동기화 할 수 있다.
|
|
volatile
키워드를 사용하면 멀티 스레딩을 쓰더라도 초기화되는 과정이 올바르게 진행된다.- DCL은 자바 5보다 낮은 버전을 사용한다면 동기화가 제대로 안될 수 있다.
싱글턴 패턴의 문제
- 모든 메소드와 변수가 static으로 선언된 클래스를 만들어도 결과적으로 같지만 자바의 정적 초기화를 처리하는 방법으로 인해 디버깅이 어려울 수 있다.
- 다른 이름의 클래스 로더가 2개 이상이라면 같은 클래스르 ㄹ여러 번 로딩하게되어 싱글턴에 적용 시 인스턴스가 여러개 만들어 질 수 있다.
- 리플렉션, 직렬화, 역직렬화도 싱글턴에서 문제가 될 수 있다.
- Singleton에 의존하는 객체는 전부 하나의 객체에 단단하게 결합되므로 느슨한 결합 원칙에 위배된다.
enum 활용하기
언급된 동기화 문데, 클래스 로딩 문제, 리플렉션, 직렬화와 역질렬화 문제 등은 enum으로 싱글턴을 생성해서 해결할 수 있다.
|
|
정리
- 싱글턴 패턴은 클래스 인스턴스를 하나만 만들고 그 인스턴스로의 전역 접근을 제공하는 방법.
- 어떤 클래스에 싱글턴 패턴을 적용하면 그 클래스의 인스턴스가 1개만 있도록 할 수 있다.
- 싱글턴 패턴을 사용하면 하나뿐인 인스턴스를 어디서든지 접근할 수 있도록 할 수 있다.
- 자바에서 싱글턴 패턴을 구현할 때는 private 생성자와 정적 메소드, 정적 변수를 사용한다.
- 멀티 스레드를 사용하는 애플리케이션에서는 속도와 자원 문제를 파악해보고 적절한 구현법을 사용한다.
- 사실 모든 애플리케이션에서 멀티스레딩을 쓸 수 있다고 생각해야한다.
- DCL을 써서 구현하면 자바 5 이전에 나온 버전에서는 스레드 관련 문제가 생길 수 있다.
- 클래스 로더가 여러 개 있으면 싱글턴이 제대로 작동하지 않고, 여러개의 인스턴스가 생길 수 있다.
- 자바의 enum을 쓰면 간단하게 싱글턴을 구현할 수 있다.