본 포스팅은
정보 제공용이 아닌
여러 개발자 블로그들의 자료를
바탕으로 보고 배운 것을 직접 정리해본 포스트 입니다.
1. 싱글톤 패턴이란?
- 어플리케이션이 시작될 때 해당 클래스가 "최초 한번" 메모리를 할당하고(static) 그 메모리에 인스턴스를 만들어 사용하는 디자인 패턴이다.
- 생성자가 여러번 호출되더라도 실제로 생성되는 객체는 하나뿐이고 최초 생성 이후에 호출된 생성자는 최초의 생성한 객체를 반환한다.
2. 왜 사용하는 것인가?
- A라는 클래스가 있다하자.
- B클래스와 C클래스에서 필요에 의해 A클래스의 생성자를 호출해 인스턴스를 생성하게 되면 B와 C에서 생성된 A클래스가 메모리에 각각 올라가버린다.
- 하지만 이 싱글톤 패턴을 사용하면 A 객체 생성을 단 한 번으로 제한하여 같은 객체의 인스턴스가 무분별하게 복제되는 경우를 방지할 수 있게 된다.
3. 싱글톤 패턴의 장점
- 1) 메모리 낭비를 방지할 수 있다.
- 2) 싱글톤으로 만들어진 클래스와 다른 클래스의 인스턴스들의 데이터 공유가 쉽다.
- 3) 인스턴스가 절대적으로 한개만 존재하는 것을 보증하기에 개발 시 실수를 줄일 수 있다.
- 4) 싱글톤 객체를 사용하지 않는 경우 인스턴스를 생성하지 않는다.
- 5) 싱글톤을 상속시킬 수 있다.
4. 싱글톤 패턴의 단점
1. 전역변수보다 사용하기가 불편하다.
2. 싱글톤의 역할이 커질수록 결합도가 높아져 객체 지향 설계 원칙에 어긋날 수 있다.
3. 멀티쓰레드 환경에서 컨트롤이 어렵다.
4. 객체의 파괴 시점을 컨트롤하기 어려울 수 있다.
5. 싱글톤 구현하기
** 기본 싱글톤 패턴
//기본 싱글톤 패턴
public class Singleton {
// 단 1개만 존재해야 하는 객체의 인스턴스, static 으로 선언
private static Singleton instance;
// private 생성자로 외부에서 객체 생성을 막음
private Singleton() {}
// 외부에서는 getInstance() 로 instance 를 반환
public static Singleton getInstance() {
// instance 가 null 일 때만 생성
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
- 1) 외부에서 생성할 수 없게 생성자를 private으로 선언한다.
- 2) getInstance() 메서드로만 instance에 접근이 가능하게 만든다.
- 3) instance는 static으로 선언해두고
- 4) getInstance가 최초로 호출될 때는 null이므로 생성해주고 이후에는 생성해둔 instance를 반환하게 하여
- 5) 여러 번 호출하더라도 최초 생성한 instance만 반환하게 한다.
하지만 위와 같은 늦은 방법은 멀티 스레드 환경에서 Tread-Safe를 보장해주지 않는다.
예를 들어 두 스레드가 동시에 싱글톤 인스턴스에 접근할 때 생성이 안된 것을 확인하고 생성시 동시에 중복으로 생성될 수 있는 문제가 있다. 이런 문제를 해결하기 위해 java에서 스레드를 지원하는 Synchrozied를 사용한다.
** synchrozied를 사용한 싱글톤 패턴
//synchrozied를 사용한 싱글톤 패턴
public class Singleton {
private static Singleton instance;
private Singleton() {}
// synchronzied 스레드 동기화를 사용하며 멀티 스레드 환경에서의 동시성 문제 해결
public static synchronzied Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
- synchrozied를 사용하여 여러 스레드가 getInstance() 메서드에 동시에 접근하는 것을 방지할 수 있다.
- 하지만 synchrozied의 단점은 속도가 매우 느려져 성능이 저하된다는 점이다.
** double-check를 사용한 싱글턴 패턴
// Double-check를 사용한 싱글톤 패턴
public class Singleton {
private volatile static Singleton instance;
private Sigleton() {}
// Double check
public Singleton getInstance() {
// instance 가 null 인 경우
if(instance == null) {
// synchronized 를 적용한 후, 한번 더 instance 가 null 인지를 체크
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return uniqueInstance;
}
}
Double-check는 말 그대로 두 번 체크 하는 방법으로 synchrozied를 사용하기 전에 instance가 null인지를 먼저 체크한 후 synchrozied를 사용하여 스레드를 동기화한 후 null인지 한번 더 체크하는 방법이다. 이러면 synchrozied의 속도 저하를 어느정도 개선이 가능하다.
** holder를 사용한 싱글톤 패턴
// holder를 사용한 싱글톤 패턴
public class Singleton {
private Singleton() {}
// private static inner class 인 LazyHolder
private static class LazyHolder() {
// LazyHolder 클래스 초기화 과정에서 JVM 이 Thread-Safe 하게 instance 를 생성
private static final Singleton instance = new Singleton();
}
// LazyHolder 의 instance 에 접근하여 반환
public static Singleton getInstance() {
return LazyHolder.instance;
}
}
- 이 싱글톤 패턴은 JAVA 진영에서 가장 많이, 일반적으로 사용하는 패턴이다.
- 클래스 안에 클래스(holder)를 두어 JVM의 Class loader 매커니즘과 class가 로드되는 시점을 이용한 방법.
- 개발자가 직접 동기화 문제에 코드를 작성하여 만듦으로 생기는 문제를 JVM의 클래스 초기화 과정에서 보장되는 원자적 특성을 이용하여 싱글턴 초기화 문제의 책임을 JVM에 떠 넘기는 패턴이다.
♣ 참고 및 인용
'웹 개발 한걸음' 카테고리의 다른 글
[Mybatis] 마이바티스 파헤쳐보기 - 2 - (0) | 2021.04.28 |
---|---|
[Mybatis] 마이바티스 파헤쳐보기 - 1 - (0) | 2021.04.22 |
[JavaScript] AJAX / XMLHttpRequest 파헤쳐보기 (0) | 2021.04.14 |
[JavaScript] 자바스크립트 기초 배워보기 (0) | 2021.04.06 |
[Servlet] 게시물 삭제 구현하기 (0) | 2021.04.05 |