[JAVA] 싱글턴(Singleton)

현서황·2024년 9월 20일

JAVA

목록 보기
11/16

자바의 싱글턴 패턴은 하나의 클래스에 대해 단 하나의 인스턴스만 생성되는 것을 보장하는 디자인 패턴이다.
이 패턴은 프로그램 전체에서 동일한 객체를 사용해야할 때 유용하다.
주로 자원을 많이 사용하는 객체나 전역적으로 상태를 관리하는 객체를 만들 때 사용된다.


싱글턴 패턴의 특징

  1. 유일한 인스턴스 : 클래스가 단 하나의 인스턴스만 생성된다.
  2. 전역접근 : 생성된 인스턴스에 어디서든 접근할 수 있는 전역 접근 방법을 제공한다.
  3. 인스턴스 생성의 제어 : 클래스 외부에서 인스턴스를 생성하지 못하도록 생성자를 private으로 선언한다.

자바에서의 싱글턴 패턴 구현

  1. 생성자를 private으로 선언해 외부에서 직접 객체를 생성하지 못하도록 막는다.
  2. 클래스 내부에 자기 자신을 참조하는 private static변수를 선언한다.
  3. public static메서드를 통해 유일한 인스턴스에 접근할 수 있또록 한다.

기본 싱글턴 구현

public class Singleton {
    // 유일한 인스턴스를 저장할 정적 변수
    private static Singleton instance;

    // private 생성자: 외부에서 직접 인스턴스를 생성하지 못하게 막음
    private Singleton() {
    }

    // 전역에서 인스턴스에 접근할 수 있는 메서드
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

Singleton 클래스의 인스턴스가 처음 요청될 때만 생성되고, 이후에는 이미 생성된 인스턴스를 반환한다.


멀티 스레드 환경에서의 싱글턴

위의 기본 싱글턴 구현은 단일 스레드 환경에서는 문제가 없겠지만, 멀티스레드 환경에서는 여러 스레드가 동시에 getInstance()를 호출하면 여러 개의 인스턴스가 생성될 수 있다.
이를 방지하기 위해 synchronized키워드를 사용해 동기화된 접근을 보장할 수 있다.

synchronized는 한 번에 하나의 스레드만 해당 블록이나 메서드에 접근할 수 있도록 보장한다.

public class Singleton {
    private static Singleton instance;

    private Singleton() {
    }

    // synchronized를 사용하여 멀티스레드 환경에서 안전하게 처리
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

더블 체크 잠금

성능 저하를 줄이기 위해, 인스턴스가 생성된 후에는 동기화를 피할 수 있는 더블 체크 잠금 방식을 사용할 수 있다.
volatile키워드를 사용해 인스턴스 변수를 메모리에서 직접 읽도록 보장한다.

volatile키워드는 변수의 값을 여러 스레드가 동시에 읽고 쓸 때 발생할 수 있는 가시성 문제를 해결해준다.
즉, 이 키워드를 변수에 붙이면, 그 변수는 스레드 간에 즉시 메모리의 일관성을 유지하도록 강제된다. 수정 시 바로 메인 메모리에 기록되는 것이다. 그리고, 다른 메모리가 해당 변수를 읽을 때는 반드시 메인 메모리에서 최신 값을 가져온다.

자바에서 멀티스레드 프로그램을 작성할 때, 여러 스레드가 동시에 변수에 접근하고 수정하는 경우가 많다. 하지만 자바의 JVM과 CPU는 최적화를 위해 변수의 값을 각 스레드의 캐시에 저장하고, 필요할 때만 메인 메모리와 동기화합니다. 이로인해 한 스레드에서 변수 값을 수정하더라도 다른 스레드에서 이를 인식하지 못하는 상황이 발생할 수 있다.
이를 가시성 문제라고 한다!

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

이른 초기화

싱글턴 인스턴스를 미리 생성해두는 방식도 있다. 이 방법은 인스턴스가 필요하지 않더라도 클래스가 로드될 때 생성되므로 메모리 낭비가 있을 수 있지만, 멀티 스레드 환경에서는 안전하다.

public class Singleton {
    // 클래스 로드 시 바로 인스턴스를 생성
    private static final Singleton instance = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return instance;
    }
}

싱글턴 패턴의 사용 예

  • 로깅 시스템 : 여러 클래스에서 동일한 로그를 남기기 위해 싱글턴 로거를 사용할 수 있다.
  • 데이터베이스 연결 관리 : 데이터 베이스 연결을 하나의 인스턴스로 유지하고, 여러 곳에서 재사용할 때 유용하다.
  • 설정 정보 관리 : 애플리케이션 전역에서 사용하는 설정 파일의 내용을 싱글턴 객체로 관리하여 일관성을 유지할 수 있다.

싱글턴의 장점

  • 인스턴스 관리 : 객체가 하나만 생성되므로 메모리와 리소스를 효율적으로 사용할 수 있다.
  • 전역 상태 관리 : 애플리케이션 전체에서 공통된 객체에 접근할 수 있어 상태 관리가 쉬워진다.

싱글턴의 단점

  • 테스트 어려움 : 전역적으로 관리되는 객체는 테스트가 어려울 수 있으며, 상태가 공유되어 테스트 격리하기 어렵다.
  • 의존성 주입의 어려움 : 싱글턴을 남용하면 코드가 전역 상태에 의존하게 되어 유지보수가 어려워질 수 있다.
profile
노는 게 제일 좋은 뽀로로

0개의 댓글