본문 바로가기

디자인 패턴/생성 패턴

[생성 패턴] 싱글톤(Singleton Pattern) Java 예제

728x90

싱글톤이란?

싱글톤 패턴하나의 클래스에 대해 단 하나의 인스턴스만 생성되도록 보장하고, 이 인스턴스에 전역적 접근을 제공하는 디자인 패턴입니다. 프로그램 전체에서 동일한 객체를 공유해야 할 때 사용하는 패턴입니다. 

 

 

싱글톤 패턴이 필요한 이유

  1. 하나의 리소스에 대한 일관성 유지
    • 데이터베이스 연결 객체나 설정 파일 관리 객체처럼 하나만 존재해야 하는 리소스를 관리할 때 사용합니다.
    • 여러 객체가 동일한 자원을 동시에 수정하면 데이터 무결성에 문제가 발생할 수 있습니다.
  2. 메모리 절약 및 성능 최적화
    • 동일한 객체를 여러 번 생성하는 대신 하나의 인스턴스를 재사용하므로 메모리 낭비를 줄일 수 있습니다.
  3. 글로벌 접근 가능성 제공
    • 프로그램 어디서든지 하나의 객체에 접근해야 하는 경우에 사용합니다.

 

싱글톤 패턴의 구조

싱글톤 패턴의 기본 구조는 다음과 같습니다:

  1. 클래스 생성자를 private으로 선언해 외부에서 객체를 생성할 수 없도록 합니다.
  2. 클래스 내부에 자신의 유일한 인스턴스를 저장합니다.
  3. 인스턴스에 접근하는 정적 메서드(getInstance())를 제공합니다.

 

싱글톤 패턴 구현 방법

1. 기본 싱글톤 패턴 (Lazy Initialization)

인스턴스를 필요할 때 생성합니다.

public class Singleton {

    // 클래스 내부에 유일한 인스턴스를 저장할 정적 필드 선언
    private static Singleton instance;

    // 생성자를 private으로 선언하여 외부에서 인스턴스 생성 불가
    private Singleton() {
        System.out.println("Singleton instance created");
    }

    // 정적 메서드로 인스턴스에 접근
    public static Singleton getInstance() {
        if (instance == null) {  // 처음 호출될 때 인스턴스 생성
            instance = new Singleton();
        }
        return instance;
    }
}

 

사용 예시:

public class Main {
    public static void main(String[] args) {
        Singleton obj1 = Singleton.getInstance();
        Singleton obj2 = Singleton.getInstance();

        // 두 객체가 동일한지 확인
        System.out.println(obj1 == obj2);  // true
    }
}

 

출력:

Singleton instance created
true

 

  • 특징: 첫 번째로 getInstance()가 호출될 때 인스턴스를 생성합니다.
  • 문제점: 멀티스레드 환경에서 두 개의 스레드가 동시에 getInstance()를 호출하면 여러 개의 인스턴스가 생성될 위험이 있습니다.

 

2. Thread-safe Singleton (멀티스레드 환경에서 안전한 싱글톤)

멀티스레드 환경에서는 여러 스레드가 동시에 접근할 때 문제가 발생할 수 있습니다. 이를 해결하기 위해 동기화를 사용합니다.

public class Singleton {

    private static Singleton instance;

    private Singleton() {
        System.out.println("Singleton instance created");
    }

    // synchronized 키워드로 멀티스레드 환경에서 안전하게 만듦
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

 

문제점:

  • 성능 저하: synchronized 키워드로 인해 모든 스레드가 동시에 접근하지 못하므로 성능이 저하될 수 있습니다.

 

 

3. 이중 검증 잠금 (Double-Checked Locking)

성능 문제를 해결하기 위해 이중 검증 잠금(double-checked locking) 방식을 사용합니다.

public class Singleton {

    // volatile 키워드로 인스턴스가 완전히 생성되기 전에 참조되는 것을 방지
    private static volatile Singleton instance;

    private Singleton() {
        System.out.println("Singleton instance created");
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {  // 첫 번째 검사
                if (instance == null) {  // 두 번째 검사
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

특징:

  • volatile 키워드는 인스턴스가 생성되는 도중에 다른 스레드가 참조하지 않도록 보장합니다.
  • 성능과 멀티스레드 안전성을 동시에 확보합니다.

 

4. Bill Pugh Singleton (권장되는 방식)

내부 정적 클래스를 사용한 싱글톤 구현은 성능과 안정성이 모두 뛰어납니다. 이 방법은 클래스가 로드될 때 초기화되지 않고, 최초로 호출될 때 인스턴스가 생성됩니다.

public class Singleton {

    private Singleton() {
        System.out.println("Singleton instance created");
    }

    // 내부 정적 클래스에 유일한 인스턴스를 생성
    private static class SingletonHelper {
        private static final Singleton INSTANCE = new Singleton();
    }

    // 정적 메서드로 인스턴스에 접근
    public static Singleton getInstance() {
        return SingletonHelper.INSTANCE;
    }
}

특징:

  • 내부 클래스는 사용될 때만 로드되므로 지연 초기화(Lazy Initialization)를 보장합니다.
  • 멀티스레드 환경에서도 안전합니다.

지연 초기화필요할 때까지 객체의 초기화를 지연시키는 방법입니다.
즉, 객체나 리소스가 처음으로 사용될 때 인스턴스를 생성하여 불필요한 자원 낭비를 방지합니다.

 

싱글톤 패턴의 장단점

장점:

  1. 메모리 절약: 객체를 한 번만 생성하므로 메모리 낭비가 줄어듭니다.
  2. 일관성 유지: 여러 객체가 아닌 하나의 인스턴스를 공유하므로 데이터의 일관성을 유지할 수 있습니다.
  3. 전역적 접근: 프로그램 어디서든 동일한 인스턴스에 접근할 수 있습니다.

단점:

  1. 테스트 어려움: 객체를 하나만 생성하므로 의존성 주입(Dependency Injection)이 어려워 테스트 코드 작성이 복잡해질 수 있습니다.
  2. 의존성 문제: 하나의 객체가 프로그램 전체에서 사용되므로 클래스 간 강한 결합이 생길 수 있습니다.
  3. 멀티스레드 이슈: 잘못 구현된 싱글톤은 멀티스레드 환경에서 동시성 문제를 유발할 수 있습니다.

 

싱글톤 패턴의 사용 사례

  1. 데이터베이스 연결 객체 (DB Connection Pool)
    • 데이터베이스와 연결하는 객체는 여러 개의 인스턴스를 생성할 필요가 없으므로 싱글톤으로 구현합니다.
  2. 설정 클래스 (Configuration)
    • 애플리케이션의 설정 정보는 하나의 인스턴스로 공유하는 것이 바람직합니다.
  3. 로깅 객체 (Logger)
    • 로그 기록을 남기는 객체는 싱글톤으로 구현하여 여러 곳에서 동일한 로그 파일을 사용할 수 있도록 합니다.

 

 

정리

싱글톤 패턴은 프로그램 전역에서 하나의 인스턴스만 필요할 때 매우 유용합니다.
다만, 멀티스레드 환경에서는 주의가 필요하며 내부 클래스 방식(Bill Pugh 방식)을 사용하는 것이 성능과 안정성을 모두 보장할 수 있는 좋은 선택입니다.

728x90