1. 소개

이 빠른 기사에서는 일반 Java에서 Singleton을 구현하는 가장 널리 사용되는 두 가지 방법에 대해 설명합니다.

2. 클래스 기반 싱글톤

가장 널리 사용되는 접근 방식은 일반 클래스를 만들고 다음이 있는지 확인하여 Singleton을 구현하는 것입니다.

  • 개인 생성자
  • 유일한 인스턴스를 포함하는 정적 필드
  • 인스턴스를 얻기 위한 정적 팩토리 메소드

나중에 사용할 수 있도록 정보 속성도 추가합니다. 따라서 구현은 다음과 같습니다.

public final class ClassSingleton {

    private static ClassSingleton INSTANCE;
    private String info = "Initial info class";
    
    private ClassSingleton() {        
    }
    
    public static ClassSingleton getInstance() {
        if(INSTANCE == null) {
            INSTANCE = new ClassSingleton();
        }
        
        return INSTANCE;
    }

    // getters and setters
}

이것은 일반적인 접근 방식이지만 싱글톤을 사용하는 주된 이유인 다중 스레딩 시나리오에서 문제가 될 수 있다는 점에 유의해야 합니다 .

간단히 말해서 패턴의 핵심 원칙을 위반하는 인스턴스가 두 개 이상 발생할 수 있습니다. 이 문제에 대한 잠금 솔루션이 있지만 다음 접근 방식은 이러한 문제를 루트 수준에서 해결합니다.

3. 열거형 싱글톤

앞으로 열거형을 사용하는 또 다른 흥미로운 접근 방식에 대해 논의해 보겠습니다.

public enum EnumSingleton {
    
    INSTANCE("Initial class info"); 
 
    private String info;
 
    private EnumSingleton(String info) {
        this.info = info;
    }
 
    public EnumSingleton getInstance() {
        return INSTANCE;
    }
    
    // getters and setters
}

이 접근 방식은 enum 구현 자체에 의해 직렬화 및 스레드 안전성이 보장되어 내부적으로 단일 인스턴스만 사용 가능하도록 보장하여 클래스 기반 구현에서 지적된 문제를 수정합니다.

4. 사용법

ClassSingleton 을 사용하려면 인스턴스를 정적으로 가져와야 합니다.

ClassSingleton classSingleton1 = ClassSingleton.getInstance();

System.out.println(classSingleton1.getInfo()); //Initial class info

ClassSingleton classSingleton2 = ClassSingleton.getInstance();
classSingleton2.setInfo("New class info");

System.out.println(classSingleton1.getInfo()); //New class info
System.out.println(classSingleton2.getInfo()); //New class info

EnumSingleton다른 Java Enum처럼 사용할 수 있습니다.

EnumSingleton enumSingleton1 = EnumSingleton.INSTANCE.getInstance();

System.out.println(enumSingleton1.getInfo()); //Initial enum info

EnumSingleton enumSingleton2 = EnumSingleton.INSTANCE.getInstance();
enumSingleton2.setInfo("New enum info");

System.out.println(enumSingleton1.getInfo()); // New enum info
System.out.println(enumSingleton2.getInfo()); // New enum info

5. 일반적인 함정

싱글톤은 믿을 수 없을 정도로 단순한 디자인 패턴이며 프로그래머가 싱글톤을 만들 때 저지를 수 있는 일반적인 실수는 거의 없습니다.

AdChoices
광고

우리는 싱글톤과 관련된 두 가지 유형의 문제를 구분합니다.

  • 실존적(싱글톤이 필요한가요?)
  • 구현(제대로 구현합니까?)

5.1. 실존적 문제

개념적으로 싱글톤은 일종의 전역 변수입니다. 일반적으로 전역 변수는 피해야 한다는 것을 알고 있습니다. 특히 상태가 변경 가능한 경우에는 더욱 그렇습니다.

우리는 싱글톤을 절대 ​​사용해서는 안된다고 말하는 것이 아닙니다. 그러나 우리는 코드를 구성하는 더 효율적인 방법이 있을 수 있다고 말하고 있습니다.

메서드의 구현이 싱글톤 개체에 의존하는 경우 매개 변수로 전달하지 않는 이유는 무엇입니까? 이 경우 메서드가 의존하는 대상을 명시적으로 보여줍니다. 결과적으로 테스트를 수행할 때 이러한 의존성을 쉽게 조롱할 수 있습니다(필요한 경우).

예를 들어 싱글톤은 종종 응용 프로그램의 구성 데이터(즉, 저장소에 대한 연결)를 포함하는 데 사용됩니다. 전역 개체로 사용하면 테스트 환경에 대한 구성을 선택하기가 어려워집니다.

AdChoices
광고

따라서 테스트를 실행할 때 프로덕션 데이터베이스가 테스트 데이터로 손상되어 거의 허용되지 않습니다.

싱글톤이 필요한 경우 인스턴스화를 다른 클래스(일종의 팩토리)에 Delegation할 가능성을 고려할 수 있습니다.

5.2. 구현 문제

싱글톤이 매우 단순해 보이지만 구현에는 다양한 문제가 있을 수 있습니다. 모든 결과는 클래스의 인스턴스가 둘 이상일 수 있다는 사실입니다.

동기화
위에서 제시한 개인 생성자를 사용한 구현은 스레드로부터 안전하지 않습니다. 단일 스레드 환경에서는 잘 작동하지만 다중 스레드 환경에서는 작업의 원자성을 보장하기 위해 동기화 기술을 사용해야 합니다.

public synchronized static ClassSingleton getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new ClassSingleton();
    }
    return INSTANCE;
}

메서드 선언에서 동기화 된 키워드에 유의하십시오  . 메서드 본문에는 여러 작업(비교, 인스턴스화 및 반환)이 있습니다.

동기화가 없으면 두 스레드가 INSTANCE == null 표현식이 두 스레드 모두에 대해 true  로 평가되고 결과적으로 ClassSingleton 의 두 인스턴스가  생성되는 방식으로 실행을 인터리브할 가능성이 있습니다.

동기화  는 성능에 상당한 영향을 미칠 수 있습니다. 이 코드가 자주 호출되는 경우 지연 초기화 또는 이중 확인 잠금 과 같은 다양한 기술을 사용하여 속도를 높여야 합니다(컴파일러 최적화로 인해 예상대로 작동하지 않을 수 있음). 예제 " Double-Checked Locking with Singleton " 에서 자세한 내용을 볼 수 있습니다 .

여러 인스턴스
싱글톤의 여러 인스턴스로 끝날 수 있는 JVM 자체와 관련된 싱글톤에는 몇 가지 다른 문제가 있습니다. 이러한 문제는 매우 미묘하며 각각에 대해 간략하게 설명하겠습니다.

  1. 싱글톤은 JVM마다 고유해야 합니다. 이는 분산 시스템 또는 내부가 분산 기술을 기반으로 하는 시스템에서 문제가 될 수 있습니다.
  2. 모든 클래스 로더는 해당 버전의 싱글톤을 로드할 수 있습니다.
  3. 아무도 그것에 대한 참조를 보유하지 않으면 싱글톤은 가비지 수집될 수 있습니다. 이 문제로 인해 한 번에 여러 싱글톤 인스턴스가 존재하지는 않지만 다시 만들 때 인스턴스가 이전 버전과 다를 수 있습니다.

6. 결론

이 빠른 사용방법(예제)에서는 핵심 Java만 사용하여 Singleton 패턴을 구현하는 방법과 일관성을 유지하는 방법 및 이러한 구현을 사용하는 방법에 중점을 두었습니다.

이러한 예제의 전체 구현은 GitHub 에서 찾을 수 있습니다 .

Generic footer banner