면접대비
싱클톤 패턴에 대해
빵파레2
2019. 9. 9. 21:36
싱글톤 패턴이란?
- 해당 클래스의 객체를 하나만 생성 하도록 하며, 생성된 객체를 어디에서든지 참조할 수 있도록 하는 패턴
- 애플리케이션이 시작될 때 어떤 클래스가 최초 한번만 메모리를 할당하고(Static) 그 메모리에 인스턴스를 만들어 사용하는 디자인 패턴.
- 인스턴스를 불필요하게 생성하지 않고 오직 JVM 내에서 한 개의 인스턴스만 생성하여 재사용을 위해 사용되는 디자인패턴이다.
싱글톤 패턴을 사용하는 이유
- 고정된 메모리 영역을 사용하도록 단 한번 new 연산자로 인스턴스를 얻어오기 때문에 메모리의 낭비를 줄일 수 있다.
- 전역변수로 선언되고 전역메서드로 호출 하기 때문에 다른 클래스에서 사용하기 쉽다.
- 인스턴스가 절대적으로 한개만 존재하는 것을 보장하고 싶을 경우 사용한다.
- 두 번째 이용시부터는 로딩 시간이 현저하게 줄어 성능에 이점이 있다.
싱글톤 패턴의 문제점
- 싱글톤 인스턴스로 너무 많은 데이터를 공유시키면 다른 클래스의 인스턴스들간의 결합도가 높아져 객체 지향 설계 원칙에 어긋날 수 있다. (즉, 유지보수가 힘들어 질 수 있다.)
- 멀티쓰레드 환경에서 동기화 처리를 하지 않으면 문제가 발생할 수 있다. (thread의 동시 접근 시 경우를 대비해 Thread-safe하게 작성해야 할 필요가 있다.)
- private 생성자를 갖고 있기 때문에 상속할 수 없다. (객체지향의 장점인 다형성을 이용할 수 없다.)
- 테스트 하기 힘들다.
싱글톤 구현 방식
1. 고전적인 방식의 Singleton 패턴
public class Singleton {
private static Singleton instance;
// 접근 제한자가 private으로 설정된 생성자
private Singleton(){
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 위의 코드는 멀티쓰레드 환경에서 문제가 발생할 수 있다.
- A라는 Thread와 B라는 Thread가 실행되고 있다고 가정할 때
- Thread A가 if (instance == null) 까지 진행한 상황에서 제어권이 Thread B로 넘어간 경우 Thread B역시 if (instance == null)가 수행 되어 인스턴스가 2개가 생성되는 문제가 발생한다.
2. synchronized 를 이용한 Singleton 패턴
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 쓰레드 동기화 문제의 가장 쉬운 해결방법은 synchronized 키워드 이다.
- 단일 쓰레드가 대상 메소드를 호출시작 ~ 종료까지 다른 쓰레드가 접근하지 못하도록 lock 을 하기 때문에 위의 예제 같이 getInstance()메소드를 synchronized로 처리하면 멀티 쓰레드에서 동시 접근으로 인한 인스턴스 중복 생성 문제는 해결된다.
- 하지만, synchronized getinstance()의 경우 인스턴스를 리턴 받을 때마다 Thread동기화 때문에 불필요하게 lock이 걸리게 되어 비용 낭비가 크다. (실제로 고전적인 방식에서 인스턴스가 2개 이상 생성될 확률은 매우 적다.)
- 즉, 인스턴스 할당시점만 synchronized 처리되면 될 문제를 getInstance() 메서드 전체에 synchronized 처리하여 성능문제를 발생시킨다.
3. static 초기화를 이용한 Singleton 패턴
public class Singleton {
private static Singleton instance = new Singleton(); // static 초기화 시 바로 할당
private Singleton(){
}
public static Singleton getInstance() {
return instance;
}
}
- 위의 방식은 멀티 쓰레드 환경에서 야기되는 모든 문제를 해결한다.
- Thread-safe 하며 소스가 간결하고 성능도 좋다.
- Thread가 getinstance()를 호출하는 시점이 아닌, Class가 로딩되는 시점. 즉 Static영역의 데이터 로딩시점에 private static Singleton instance = new Singleton(); 를 호출하여 하나의 인스턴스만 생성되는 것을 보장한다.
- 하지만, 실제로 사용할지 안할지 모르는 인스턴스를 굳이 미리 만들어 놓는 것이 옳지 않은 방법일 수 있다.
- 프로그램이 인스턴스를 필요로 하는 시점이 아니라 사전에 생성하는 것은 메모리의 낭비일 수 있다.
4. LazyHolder Singleton 패턴
public class Singleton {
private Singleton(){
}
public static Singleton getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
- 가장 완벽하다고 평가받는 방법이다. JAVA 버젼에 무관하고 성능도 뛰어나다.
- 이 방법은 static 영역에 초기화를 하지만 객체가 필요한 시점까지 초기화를 미루는 방식이다. (Lazy Initialization)
- Singleton 클래스가 로딩되는 순간에는 LazyHolder 클래스를 초기화 하지 않는다.
- getInstance() 메서드에서 LazyHolder.INSTANCE를 참조하는 순간 LazyHolder Class가 로딩되어 초기화가 진행된다.
5. Enum을 통한 Singletom 패턴
public enum Singleton {
INSTANCE;
}
- Enum Singleton 은 Thread-safety 와 Serialization이 보장 된다.
- Enum은 인스턴스가 여러 개 생기지 않도록 확실하게 보장해주고 복잡한 직렬화나 리플렉션 상황에서도 직렬화가 자동으로 지원된다는 이점이 있다.
스프링에서의 싱글톤
- 기본적으로 스프링은 싱글톤 패턴으로 Bean을 관리한다. (스프링 Bean의 기본 스코프는 싱글톤)
- 싱글톤 스코프는 컨테이너 내에 한 개의 오브젝트만 만들어져서, 강제로 제거하지 않는 한 스프링 컨테이너가 존재하는 동안 계속 유지된다.
- 하지만 자바에서 구현하는 일반적인 싱글톤은 위의 내용과 같이 여러 문제점이 존재한다.
- 이러한 문제점을 극복하기 위해 스프링은 직접 싱글톤 형태의 객체를 만들고 관리하는 기능을 제공한다. (싱글톤 레지스트리)
싱클톤 레지스트리
- Spring의 ApplicationContext (컨테이너)는 Singleton을 저장하고 관리하는 싱글톤 레지스트리 이다.
- 싱글톤 레지스트리의 장점은 static 메소드와 private 생성자를 사용해야 하는 비정상적인 클래스가 아니라 평범한 자바 클래스를 싱글톤으로 활용하게 해준다는 점이다.
- 평범한 자바 클래스라도 IoC방식의 컨테이너를 사용해서 객체의 생성과 관계설정, 사용 등에 대한 제어권을 컨테이너에게 넘기면 손쉽게 싱글톤 방식으로 만들어질 수 있다.
스프링에서 빈을 싱글톤으로 생성하는 이유
- 스프링은 주로 자바 엔터프라이즈 개발을 위해 사용하는 프레임워크이다. (즉, 사용자가 많은 환경이다.)
- 하나의 요청(Request)을 처리하기 위해서는 프리젠테이션 로직, 비즈니스 로직, 데이터 엑세스 로직 등 다양한 기능을 담당하는 객체들이 수행되어야 한다.
- 클라이언트로부터 이러한 요청이 올 때마다 각 기능을 담당하는 객체들을 새로 만들어 사용한다면 서버의 부하를 감당하기 힘들 수 있다.
- 따라서 스프링에서는 Servlet 클래스 당 하나의 오브젝트만 만들어 주고, 사용자의 요청을 담당하는 여러 스레드에서 하나의 오브젝트를 공유해서 사용한다.
참고자료
https://javaplant.tistory.com/21
https://jeong-pro.tistory.com/86
http://blog.naver.com/PostView.nhn?blogId=kjy6268&logNo=50107958407