싱글통 패턴이란?
제가 이걸 이해한 방식은 - 하나의 프로그램에서 단 하나의 객체만을 만들기 위한 패턴.
즉, 객체가 필요할때 새로운 객체를 만드는 것이 아니라 이미 어딘가에 만들어진 객체를 불러와서 단하나만의 객체를 사용하는것 입니다.
Calculator calculator1 = new Calculator();
Calculator calculator2 = new Calculator();
이런 코드가 있을으면 calculator1이 참조한 객체와 calculator2 가 참조한 객체는 원래라면 서로 다른 객체를 가르키고 있을 것 이다.
calculator1 != calculator2
하지만 싱글톤패턴을 도입하면 이 두가지 변수가 참조하는 객체가 같은 객체이고 그 객체는 해당 프로그램에서 유일하게 단 하나만 존재하는것입니다.
calculator1 = calculator2
이런 싱글톤을 사용하는 이유는 다음과 같습니다.
- 리소스 관리 및 접근 제어 : 한번에 하나의 인스턴스만이 자원을 사용하도록 보장함으로 써, 리소스의 과도한 사용을 방지 할 수 있다.
- 메모리 효율성 : 싱글톤은 필요한 시점에만 인스턴스를 생성하고, 이후에는 동일 인스턴스를 재사용한다.
이는 메모리 사용을 줄이고 시스템의 전반적인 효울성을 높이는데 도움이 됩니다. - 공유 상태의 일관성 : 프로그램 안에서의 전역 상태를 유지하기 때문에 , 여러 컴포넌트 간에 상태를 공유하고 일관성을 유지하는데 유용하다.
예를 들어서 데이터 베이스에 접속하는 연결 모듈이있다고 가정해보자.
아직 우리는 이런 연결 과정을 배우진 않았지만, 꽤나 비용이 많이 드는 작업일 수 있다는 것을 생각 해볼 수 있다.
그리고 한변 연결한 DB connection을 여러곳에서 불러서 사용하면 되지 굳이 여러번 생성해서 매번 호출 할 필요는 없기때문에 이런 곳에 싱글톤을 적용한다면 최초의 한번 인스턴스를 만들면 이후에는 만들지 않아도 괜찮은 상황이 나온다고 합니다.
패턴의 구현
1.Eager Initialization
public class Singleton {
public static final Singleton INSTANCE = new Singleton();
private Singleton() {
// private 생성자 외부에서 new 사용 X
}
public static Singleton getInstance() {
return INSTANCE;
}
}
해당 방식은 Singleton.INSTANCE로 쉬운 접근성을 갖고, 간결하고 명백하다는 것이 특징이며, 해당 클래스가 싱글톤임이 API에 명백히 드러난다는 점이 가장 큰 특징입니다. 또한 static final 이라 멀티 쓰레드 환경에서 안전하지만
당장 객체를 사용하지 않더라도 메모리에 적재하기 때문에 공간 자원 낭비가 발생 할 수있습니다.
또한 예외 처리가 불가 하다는 특징이 있습니다.
Static block initialization
class Singleton {
// 싱글톤 클래스 객체를 담을 인스턴스 변수
private static Singleton instance;
// 생성자를 private로 선언 (외부에서 new 사용 X)
private Singleton() {}
// static 블록을 이용해 예외 처리
static {
try {
instance = new Singleton();
} catch (Exception e) {
throw new RuntimeException("싱글톤 객체 생성 오류");
}
}
public static Singleton getInstance() {
return instance;
}
}
static block 을 이용해 예외 처리를 할 수 있습니다
(static block : 클래스가 로딩되고 클래스 변수가 준비된 후 자동으로 실행되는 블록)
그러나 여전히 static의 특성으로 사용하지 않는 공간을 차지한다는 단점이 존재 합니다.
Lazy initinalization
class Singleton {
// 싱글톤 클래스 객체를 담을 인스턴스 변수
private static Singleton instance;
// 생성자를 private로 선언 (외부에서 new 사용 X)
private Singleton() {}
// 외부에서 정적 메서드를 호출하면 그제서야 초기화 진행 (lazy)
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton(); // 오직 1개의 객체만 생성
}
return instance;
}
}
메서드를 호출했을 때 인스턴스의 null 유무에 따라 초기화 하거나 있는걸 반환 하는 방법
객체 생성에 대한 관리를 내부적으로 관리하여 미사용 고정 메모리 차지 한계를 극복하였습니다.
하지만 치명적인 문제점을 가지고있습니다.
멀티 쓰레드 환경에서 동시성문제가 발생하기 때문입니다.
- 쓰레드 A와 B가 존재한다고 가정합니다.
- 쓰레드 A가 if문을 평가하고 인스턴스 생성 코드로 진입(아직 초기화X)
- 그런데 그때 스레드 B가 if 문을 평가합니다. 아직 A가 인스턴스화 코드를 동작 하지 않아서 B도 조건문을 통과하게 됩니다.
- 그러면 결과적으로 A와 B가 인스턴스 초기화를 두번 실행하는 모습이 됩니다.
A 스레드 에서 진입
if(instance == null){
instance = new Singleton(); A쓰레드 인스턴스 화 하기 직전
}
B 스레드 에서 진입 얘 입장에선 아직 만들어 진건 아니니까
if(instance == null){
instance = new Singleton(); 똑같이 들어와서 객체 생성.
}
실제로 코드를 작성하고 동작해보면
// 싱글톤 객체
class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton(); // 오직 1개의 객체만 생성
}
return instance;
}
}
public class Main {
public static void main(String[] args) {
// 1. 싱글톤 객체를 담을 배열
Singleton[] singleton = new Singleton[10];
// 2. 스레드 풀 생성
ExecutorService service = Executors.newCachedThreadPool();
// 3. 반복문을 통해 10개의 스레드가 동시에 인스턴스 생성
for (int i = 0; i < 10; i++) {
final int num = i;
service.submit(() -> {
singleton[num] = Singleton.getInstance();
});
}
// 4. 종료
service.shutdown();
// 5. 싱글톤 객체 주소 출력
for(Singleton s : singleton) {
System.out.println(s.toString());
}
}
}
아래 사진처럼 진짜 인스턴스가 두개가 생성 된것을 확인 할 수있습니다.

이를 해결하기 위해 두가지 방법이있는데
- (Thread safe initialization) synchronized 키워드를 사용해 메서드에 쓰레드들이 하나하나씩 만 접근 가능하게 설정하는 방법 (동기화) 하지만 성능 하락이 발생한다.
- (Double-Checked Locking) volatile 키워드를 사용하는 것인데 JVM에 대한 깊은 이해가 필요하고 버전에 따라 여전히 같은문제가 발생 할 수 있기에 지양하는 방법이라고 한다.
Bill Pugh Solution (LazyHolder)
class Singleton {
private Singleton() {}
// static 내부 클래스를 이용
// Holder로 만들어, 클래스가 메모리에 로드되지 않고 getInstance 메서드가 호출되어야 로드됨
private static class SingleInstanceHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingleInstanceHolder.INSTANCE;
}
}
권장 되는 두가지 방법 중 하나
멀티쓰레드 환경에서 안전하고 Lazy Loading(나중에 객체 생성) 도 가능한 완벽한 싱글톤 기법
클래스 안에 내부 클래스(holder)를 두어 JVM의 클래스 로더 매커니즘과 클래스가 로드되는 시점을 이용한 방법 (스레드 세이프함)
이라고 하는데 솔직히 이부분은 조금 이해가 가지 않습니다.
이 부분은 제가 참고한 블로그를 보시면서 따로 학습 하실분들은 해보셔도 좋을 듯합니다.
Enum 이용
enum SingletonEnum {
INSTANCE;
private final Client dbClient;
SingletonEnum() {
dbClient = Database.getClient();
}
public static SingletonEnum getInstance() {
return INSTANCE;
}
public Client getClient() {
return dbClient;
}
}
public class Main {
public static void main(String[] args) {
SingletonEnum singleton = SingletonEnum.getInstance();
singleton.getClient();
}
}
드디어 나온 enum입니다.
이걸 보니 알게된건데 enum이라서 싱글톤이 아니라 enum으로 싱글톤 패턴을 구현 할 수 있는 거였습니다.
enum은 애초에 맴버를 만들 때 private으로 만들고 한번만 초기화 하기 때문에 쓰레드 세이프 하다고 합니다.
또한 enum안에서 상수 뿐 아니라 메서드나 변수를 선언해 사용가능 하기 때문에 이를 이용해 싱글톤 클래스 처럼 응용이 가능합니다.
다만 클래스 상속이 필요할때 enum외에 클래스 상속은 불가능 하다는 점을 주의 해야 합니다
이부분도 처음에 조금 이해가 가지 않는 부분이있어 gpt와 조금 추가 학습을 해주었습니다.
우선
INSTANCE가 무엇을 하는 키워드 인지 몰랐습니다.
저 코드를 하나씩 뜯어보면, enum은 사실 “클래스 + static final 객체”다 라는것을 이해하시면 좋을거같습니다.
해당 코드를 컴파일 해보면. 이런 클래스를 내부적으로 만든다고 합니다.
final class SingletonEnum extends Enum<SingletonEnum> {
public static final SingletonEnum INSTANCE;
private final Client dbClient;
static {
INSTANCE = new SingletonEnum(); // JVM이 자동 생성
}
private SingletonEnum() {
super("INSTANCE", 0);
dbClient = Database.getClient();
}
public static SingletonEnum getInstance() {
return INSTANCE;
}
}
INSTANCE; 이 한줄이
public static final SingletonEnum INSTANCE = new SingletonEnum();
이 코드와 동일한 의미를 가지고 있습니다
이후 처음 클래스를 로딩할때 생성자를 실행하고 생성자 안에 코드가 실행되어
dbClient = Database.getClient()
dbClient 안에 커넥션 인스턴스가 계속해서 존재 할 수 있게 되는것입니다.
여기서 제가 느낀 추가적인 궁금증은 그럼 이 객체는 GC의 대상이 안되는것인가??입니다
그에 대한 정답은 (O) 대상이 되지 않습니다.
그 이유는 static 필드로 이는 Heap영역에 있는것이아닌 Method Area에 존재하기 때문입니다.
GC 는 Heap영역에 참조가 끊어진 데이터들만 정리한다는 것을 저번 포스팅에서 확인한 적있습니다.
Garbage Collection(GC) 이란?
Garbage collection 이란??저번에 학습했던 Java JVM의 메모리 영역에 대해서학습 했었습니다.그중 Java Visualizer 사이트에서 실제 메모리에 어떻게 데이터가 쌓이는지 확인 하는 중, 그럼 스택 에어리어
jamj.tistory.com
느낀점
enum을 학습하다가 싱글톤 패턴이라는 개념이 나와 이게 정확히 뭐를 의미하는 것이지? 의문을 가졌습니다.
왜냐하면 이전에 fastify 프레임워크를 학습하다 싱글톤이라는 개념이 나왔는데, 인스턴스가 무엇인지? 흐름이 이해가 잘 가지않았습니다.
하지만 지금은 자바를 처음부터 침착하게 배우고있고 과제에 적용도 해보고 있기때문에 조금 더 학습하는데 용이할것이라 생각하고 학습을 진행했습니다.
참고자료
[Java] 자바에서 싱글톤(Singleton)패턴을 적용하는 방법
싱글톤(Singleton) 싱글톤(Singleton)은 특정 클래스의 인스턴스가 애플리케이션 내에서 단 하나만 존재하도록 보장하는 패턴이다. 싱글톤은 전역 상태를 생성하거나, 리소스를 공유하는 데 유용하며
olrlobt.tistory.com
💠 싱글톤(Singleton) 패턴 - 꼼꼼하게 알아보자
Singleton Pattern 싱글톤 패턴은 디자인 패턴들 중에서 가장 개념적으로 간단한 패턴이다. 하지만 간단한 만큼 이 패턴에 대해 코드만 던져주고 끝내버리는 경우가 있어, 어디에 쓰이는지 어떠한 문
inpa.tistory.com
'TIL' 카테고리의 다른 글
| enum이란 무엇인가? (0) | 2026.01.13 |
|---|---|
| Flowchart에 대하여. (0) | 2026.01.13 |
| Garbage Collection(GC) 이란? (0) | 2026.01.12 |
| 객체지향 이해하기 -1 (배경 지식) (0) | 2026.01.09 |
| Hello Java! (1) | 2026.01.08 |