싱글턴 패턴(Singleton Pattern) 이란?

싱글턴 패턴(Singleton Pattern) 이란 생성자를 여러 차례를 하나로 유지하는 것을 의미합니다. 객체가 최초로 생성된 이후에 생성자나 객체 생성 메서드는 기존에 만들어진 객체를 반환합니다.

프로그램 전체에서 객체를 단 하나만 생성하고, 어디서든 그 객체에 접근할 수 있게 한다.

예를 들어 DB 연결 객체가 있다고 가정했을 때,

DBConnection db1 = new DBConnection();
DBConnection db2 = new DBConnection();
DBConnection db3 = new DBConnection();

아래와 같은 문제점이 발생할 수 있다

  • DB 연결 객체가 계속 생성됨
  • 메모리 낭비
  • 연결 충돌 가능 (?)

그래서 DBConnection은 프로그램 전체에서 하나만 존재 하도록 만들고 싶을 때 사용하는 것이 싱글턴 패턴 이다.

싱글턴 패턴 구조

싱글턴의 3가지 규칙

  1. 생성자를 private로 막는다.
  2. static으로 자기 자신 객체를 저장한다.
  3. 객체를 가져오는 메서드를 제공한다.
class Singleton {
	private static Singleton instance;
	private Singleton() {}
	public static Singleton getInstance() {
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
 
// s1 == s2 -> true

싱글턴 구현 방식

  1. 즉시 생성(Eager)
private static Singleton instance = new Singleton();
  • 필드에 자기자신 타입의 인스턴스를 필드로 하나 보유하고, 바로 초기화하는 방식
  • 장점: Thread-safe, 생성 속도가 빠름
  • 단점: 사용하지 않아도 생성되어 메모리 공간을 차지함. 생성된 인스턴스를 사용하지 않더라도 인스턴스가 항상 무조건 생성되어 예외 처리를 할 수 있는 방법이 없음.
  1. 지연 생성(Lazy)
if(instance == null)
  • 처음 필드를 생성할 때 초기화하지 않고 getInstance() 메소드를 호출하게 되면 초기화를 시도하는 방식
  • 장점: 메모리 절약
  • 단점: Thread-safe 하지 않음, 여러개의 쓰레드가 동시에 요청할 경우 싱글톤의 유일성을 보장할 수 없음 (다중 인스턴스가 생길 수 있음)
  1. synchronized 방식
public static synchronized Singleton getInstance()
  • Lazy 방식을 보완할 수 있는 방법, 쓰레드가 이 인스턴스를 생성하는 임계영역에 진입할 때 순차적으로 진입할 수 있도록 하는 방식
  • 동시에 여러 쓰레드가 인스턴스 생성 메소드에 접근하지 못하도록 하나의 인스턴스만 생성됨을 보장함
  • 장점: Thread-safe
  • 단점: synchronized 키워드 사용으로 성능이 저하될 수 있음 (하나의 쓰레드만 초기화 임계영역에 접근 가능하기 때문)
  1. Double Checked Locking Pattern (DCLP)
if(instance == null) {
	synchronized(Singleton.class) {
		if(instance == null) {
			instance = new Singleton();
		}
	}
}
  • 첫번째로 인스턴스가 null인지 체크해 인스턴스가 이미 생성되었는지 아닌지를 체크하고, 생성된게 없는 경우에만 synchronized 키워드에 기반하여 Thread-safe하게 인스턴스를 생성하는 방식
  • 장점: synchronized 방식보다 성능 향상
  • 단점: 메소드 내부의 코드가 많아짐 (가독성 저하)
  • volatile

싱글턴과 static의 차이점

static은 객체가 없는 클래스 수준 기능이고 다형성이나 인터페이스 구현이 어려움. 반면 싱글턴은 객체가 존재하기 때문에 객체지향 설계가 가능하고 의존성 주입이나 확장이 가능함.

싱글턴 패턴의 장단점

장점

  1. 최초 한번의 new 연산자를 통해 고정된 메모리 영역을 사용하기 때문에 객체에 접근할 때 메모리 낭비를 방지할 수 있고, 이미 생성된 인스턴스를 활용하기 때문에 속도가 빠르다.
  2. 클래스간 데이터 공유가 쉽다. 전역으로 사용되는 인스턴스기 때문에 다른 클래스 인스턴스들이 접근하여 사용할 수 있다. (다만 동시성 문제가 발생할 수 있음)
  3. 도메인 관점에서 인스턴스가 한개만 존재하는 것을 보증하고 싶을 때도 싱글턴 패턴 사용

단점

  1. 동시성 문제 해결을 위해 synchronized 키워드를 사용해야 해서 복잡성이 올라간다.
  2. 테스트하기 어렵다. 인스턴스는 자원을 공유하고 있기 때문에 테스트가 격리된 환경에서 수행되려면 매번 인스턴스의 상태를 초기화시켜주어야 한다.
  3. 의존 관계상 클라이언트가 구체 클래스에 의존하게 된다. (?)
  4. 자식 클래스를 만들 수 없다.
  5. 내부 상태를 변경하기 어렵다.