Singleton : 인스턴스 한 개만 만들기
특정 클래스 인스턴스가 반드시 하나만 존재해야 하며 이를 보증하고 싶을 때 사용한다. 어디서든 해당 인스턴스 접근할 수 있게 보장하는 디자인 패턴이다.
목 차
Singleton 패턴의 구성요소
싱글톤 패턴 적용할 객체 클래스(아래 예시에서는 Singleton 객체 클래스)가 필요하며, 해당 객체 클래스는 아래와 같은 변수와 메서드를 기본적으로 갖는다.
1. Singleton 클래스
public class Singleton {
// (1) 클래스 변수 singleton
private static Singleton singleton = new Singleton();
// (2) 생성자 Singleton
private Singleton() {}
// (3) 메서드 getInstance
public static Singleton getInstance() {
return singleton;
}
}
(1) 클래스 변수 singleton : 클래스 로딩 시 Singleton 인스턴스로 초기화 됨 (*즉시 초기화 방식 사용. 뒤에서 나올 구현 방식에서 추가 설명 확인)
(2) 생성자 Singleton : 접근 제한자가 private으로 외부에서 생성자 호출을 통해 인스턴스가 생성되는 것을 막음.
(3) 메서드 getInstance : 유일한 인스턴스를 얻기 위한 메서드. 클래스 로딩 시 이미 초기화된 객체 singleton을 반환함.
Singleton 패턴의 특징
유일한 인스턴스
인스턴스가 단 하나만 존재한다는 것이 보증된다. 이를 통해 메모리 낭비를 줄일 수 있다.
어디서든 접근 가능한 인스턴스
전역적으로 접근 가능한 인스턴스를 제공하여 필요한 여러 곳에서 동일한 자원을 사용하도록 만든다.
Singleton 패턴의 문제점
싱글톤이 가진 이점 때문에 무턱대고 싱글톤 패턴을 사용하게 된다면 예상치 못한 문제에 맞닥뜨릴 수 있다. 특히, 멀티 쓰레드(multi-thread) 환경에서 동시에 변수에 접근하게 될 때 말이다.
1. 멀티 스레스 환경에서의 동시성 문제
멀티 스레드(Multi-thread) 환경에서 인스턴스가 없을 때, 동시에 getSingleton() 메서드를 호출하여 실행하는 경우 인스턴스가 중복 생성될 위험이 있다.
2. 테스트의 어려움
싱글톤 패턴은 전역 변수로 인스턴스를 제공하여 자원을 공유게 된다. 이 때문에 테스트 환경에서 각각 격리된 인스턴스를 사용하기가 어렵다. 특히 여러명이 동시에 테스트를 진행하게 될 때 문제가 된다.
3. 리소스 누수 가능성
싱글톤 객체는 클래스(전역) 변수로 생성되어 애플리케이션이 종료되기 전까지 메모리에 유지된다. 이 때문에 많은 리소스를 사용하는 경우 메모리 누수가 발생될 수 있다. 특히, 데이터베이스 연결이나 파일을 다룰 때 싱글 톤 객체가 이를 관리하게 되면 문제가 발생할 수 있다.
4. 상속 및 유연성 문제
싱글톤 패턴은 상속이 제한적이여서 유연하게 구조를 바꾸는게 어렵기 때문에 다양한 기능을 추가하거나 하는 데 어려움이 있다.
5. 직렬화 문제
싱글콘 클래스에서 직렬화(serialization)를 사용하게되면 직렬화와 역직렬화 과전에서 새로운 인스턴스가 생성될 수 있다. 이 때문에 싱글톤 규칙이 깨질 수 있다.
Singleton 패턴의 구현 방식
싱글톤 패턴의 기본 구현 방식은 Eager Initialization(즉시 초기화) 와 Lazy Initialization(지연 초기화)가 있다. Lazy Initialization의 성능저하 문제를 보완하기 위해 Double-Checked Locking(이중 검사 잠금) 방식을 사용한다.
Eager Initialization (즉시 초기화)
애플리케이션이 실행되어 클래스 로딩 시, 싱글톤 객체를 즉시 생성하는 스레드 세이프(Thread-safe)한 초기화 방식이다. 하지만 클래스가 여부와 관계 없이 인스턴스가 생성되기 때문에 메모리 자원이 낭비될 수 있다.
public class Singleton {
// 인스턴스를 정적(static) 필드로 선언하고 클래스 로딩 시 초기화 한다.
private static Singleton singleton = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return singleton;
}
}
Lazy Initialization (지연 초기화)
실제로 인스턴스가 필요한 시점에 객체를 생성하여 메모리 자원을 효율적으로 사용하는 방식이다. 하지만 초기화 시점에 스레드 세이프를 보장하기 위해서 동기화 처리(synchronized)가 필요하기 때문에 성능 저하 문제가 발생할 수 있다.
public class Singleton {
private static Singleton singleton;
private Singleton() {}
// synchronzied 키워드를 사용하여 동기화 처리
public static synchronzied Singleton getInstance() {
if(instance == null) {
singleton = new Singleton();
}
return singleton;
}
}
Double-Checked Locking
Lazy Initialization을 효율적으로 구현하는 방식으로, 인스턴스를 생성할 때만 동기화를 사용하는 방식이다.
public class Singleton {
// 메인 메모리 저장을 위해 volatile 키워드 사용.
private static volatile Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 첫 번째 인스턴스 체크 (동기화 없이)
synchronized (Singleton.class) { // 클래스에 동기화 블록
if (singleton == null) { // 두 번째 인스턴스 체크
singleton = new Singleton();
}
}
}
return singleton;
}
}
Ref.
- 책 『JAVA 언어로 배우는 디자인 패턴 입문: 쉽게 배우는 GoF의 23가지 디자인 패턴』 (저자 : 유키 히로시 저 / 번역 : 김성훈 역, 영진닷컴)
- https://tecoble.techcourse.co.kr/post/2020-11-07-singleton/
- https://velog.io/@seongwon97/%EC%8B%B1%EA%B8%80%ED%86%A4Singleton-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80
'_ 개발' 카테고리의 다른 글
[DESIGN PATTERN/JAVA] Iterator - 이터레이터 패턴 (1) | 2024.11.10 |
---|---|
[DESIGN PATTERN/JAVA] Factory Method - 팩토리 메서드 패턴 (1) | 2024.11.09 |
객체 지향 설계 원칙 5가지 - SOLID (2) | 2024.11.08 |
[DESIGN PATTERN] 디자인 패턴이란 무엇일까 : 디자인 패턴의 정의와 분류 (13) | 2024.10.31 |
Mac 팁 - 내가 보려고 만든 맥북 단축키 #1 기본 단축키 (1) | 2024.07.26 |