Java에서 싱글 톤 패턴을 구현하는 효율적인 방법은 무엇입니까? [닫은]


812

Java에서 싱글 톤 패턴을 구현하는 효율적인 방법은 무엇입니까?


1
"자바로 싱글 톤 패턴을 구현하는 효율적인 방법은 무엇입니까?" 효율적으로 정의하십시오.
Marian Paździoch

medium.com/@kevalpatel2106/... . 이 글은 싱글 톤 패턴에서 스레드, 반사 및 직렬화 안전을 달성하는 방법에 대한 완전한 기사입니다. 이것은 싱글 톤 클래스의 장점과 한계를 이해하는 좋은 소스입니다.
Keval Patel

Joshua Bloch가 Effective Java에서 지적한 것처럼 enum singleton이 가장 좋은 방법입니다. 여기 에서는 다양한 구현을 게으른 / 열심 한 등으로 분류했습니다.
isaolmez

답변:


782

열거 형을 사용하십시오.

public enum Foo {
    INSTANCE;
}

Joshua Bloch는 Google I / O 2008에서의 효과적인 Java Reloaded 강연에서 비디오에 대한 링크 에서이 접근 방식을 설명했습니다 . 그의 프레젠테이션의 슬라이드 30-32도 참조하십시오 ( effective_java_reloaded.pdf ) :

직렬화 가능한 싱글 톤을 구현하는 올바른 방법

public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

편집 : "유효한 Java"온라인 부분 은 다음과 같이 말합니다.

"이 접근법은 더 간결하고 직렬화 기계를 무료로 제공하며 정교한 직렬화 또는 리플렉션 공격에도 불구하고 다중 인스턴스화에 대한 확실한 보증을 제공한다는 점을 제외하고는 공공 현장 접근법과 기능적으로 동일합니다. 아직 널리 채택되지는 않았지만, 단일 요소 열거 형이 싱글 톤을 구현하는 가장 좋은 방법 입니다. "


203
사람들은 열거 형을 기능이있는 클래스로보아야한다고 생각합니다. 컴파일 타임에 클래스의 인스턴스를 나열 할 수 있으면 열거 형을 사용하십시오.
Amir Arad

7
저는 개인적으로 싱글 톤 패턴을 직접 사용해야 할 필요성을 종종 찾지 않습니다. 때로는 스프링의 의존성 주입을 싱글 톤이라고하는 응용 프로그램 컨텍스트와 함께 사용합니다. 내 유틸리티 클래스에는 정적 메소드 만 포함하는 경향이 있으므로 인스턴스가 필요하지 않습니다.
Stephen Denne

3
안녕하세요, 아무도이 유형의 싱글 톤을 테스트 사례에서 조롱하고 테스트하는 방법을 말해 줄 수 있습니까? 이 유형의 가짜 싱글 톤 인스턴스를 바꾸려고했지만 할 수 없었습니다.
Ashish Sharma

29
나는 그것이 의미가 있다고 생각하지만 여전히 그것을 좋아하지 않습니다. 다른 클래스를 확장하는 싱글 톤을 어떻게 만드시겠습니까? 열거 형을 사용하면 할 수 없습니다.
chharvey

11
@ bvdb : 유연성을 많이 원한다면 처음에 싱글 톤을 구현하여 이미 망쳤습니다. 필요할 때 독립적 인 인스턴스를 만드는 기능은 그 자체로 비용이 많이 들지 않습니다.
cHao

233

사용법에 따라 몇 가지 "올바른"답변이 있습니다.

java5부터는 enum을 사용하는 것이 가장 좋습니다.

public enum Foo {
   INSTANCE;
}

Pre java5에서 가장 간단한 경우는 다음과 같습니다.

public final class Foo {

    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }

    public Object clone() throws CloneNotSupportedException{
        throw new CloneNotSupportedException("Cannot clone instance of this class");
    }
}

코드를 살펴 봅시다. 먼저, 당신은 수업이 마무리되기를 원합니다. 이 경우 final키워드를 사용하여 최종 사용자에게 알 렸습니다. 그런 다음 사용자가 자신의 Foo를 만들지 못하도록 생성자를 비공개로 만들어야합니다. 생성자에서 예외를 throw하면 사용자가 리플렉션을 사용하여 두 번째 Foo를 만들 수 없습니다. 그런 다음 private static final Foo유일한 인스턴스를 보유 할 필드와 public static Foo getInstance()이를 리턴 하는 메소드를 작성하십시오. Java 스펙은 생성자가 클래스를 처음 사용할 때만 호출되도록합니다.

객체가 크거나 구성 코드가 많고 인스턴스가 필요하기 전에 사용할 수있는 다른 액세스 가능한 정적 메서드 나 필드가있는 경우에만 지연 초기화를 사용해야합니다.

a private static class를 사용하여 인스턴스를로드 할 수 있습니다 . 코드는 다음과 같습니다.

public final class Foo {

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }
}

라인이 있기 때문에 private static final Foo INSTANCE = new Foo();클래스 FooLoader가 실제로 사용하는 경우에만 실행이 게으른 인스턴스을 담당하고,이 스레드 안전을 보장한다.

객체를 직렬화하려면 직렬화 해제로 인해 사본이 생성되지 않아야합니다.

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

이 메소드 readResolve()는 이전 프로그램 실행에서 객체가 직렬화 된 경우에도 유일한 인스턴스가 반환되도록합니다.


32
반사 확인은 쓸모가 없습니다. 다른 코드가 개인에게 리플렉션을 사용하는 경우 Game Over입니다. 이러한 오용에서 올바르게 기능을 시도 할 이유가 없습니다. 그리고 시도하면 어쨌든 불완전한 "보호"가 될 것입니다. 많은 낭비되는 코드입니다.
Wouter Coekaerts

5
> "먼저, 수업이 마무리되기를 원합니다". 누군가가 이것에 대해 자세히 설명해 주시겠습니까?
PlagueHammer

2
역 직렬화 보호가 완전히 중단되었습니다 (Effective Java 2nd Ed에 언급되어 있다고 생각합니다).
Tom Hawtin-tackline

9
-1 이것은 가장 간단한 경우가 아니며 , 고 안되고 불필요하게 복잡합니다. 모든 경우의 99.9 %에 충분한 실제로 가장 간단한 솔루션에 대한 Jonathan의 답변을 살펴보십시오.
Michael Borgwardt

2
싱글 톤이 수퍼 클래스에서 상속해야 할 때 유용합니다. 이 경우 enum 싱글 톤 패턴을 사용할 수 없습니다. enum에는 수퍼 클래스가있을 수 없으므로 인터페이스를 구현할 수 있습니다. 예를 들어 enum singleton 패턴이 옵션이 아닌 경우 Google Guava는 정적 최종 필드를 사용합니다. code.google.com/p/guava-libraries/source/browse/trunk/guava/src/…
Etienne Neveu

139

면책 조항 : 나는 모든 훌륭한 답변을 요약하고 내 말로 썼습니다.


Singleton을 구현하는 동안 우리는 두 가지 옵션이 있습니다
. 1. Lazy loading
2. Early loading

게으른 로딩은 비트 오버 헤드 (많은 솔직함)를 추가하므로 매우 큰 객체 또는 무거운 구성 코드가 있고 인스턴스가 필요하기 전에 사용할 수있는 다른 액세스 가능한 정적 메소드 또는 필드가있는 경우에만 사용하십시오 초기 로딩을 선택하는 것이 좋습니다.

싱글 톤을 구현하는 가장 간단한 방법은

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }
}

초기로드 된 싱글 톤을 제외한 모든 것이 좋습니다. 게으른로드 된 싱글 톤을 시도 할 수 있습니다

class Foo {

    // Our now_null_but_going_to_be sole hero 
    private static Foo INSTANCE = null;

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT  
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        // Creating only  when required.
        if (INSTANCE == null) {
            INSTANCE = new Foo();
        }
        return INSTANCE;
    }
}

지금까지는 좋지만 우리의 영웅은 많은 영웅을 원하는 여러 악의 실과 혼자 싸우면서 생존하지 못합니다. 사악한 멀티 스레딩으로부터 보호하십시오.

class Foo {

    private static Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        // No more tension of threads
        synchronized (Foo.class) {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
        }
        return INSTANCE;
    }
}

그러나 영웅을 보호하기에는 충분하지 않습니다. 영웅을 돕기 위해 할 수있는 최선의 방법

class Foo {

    // Pay attention to volatile
    private static volatile Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        if (INSTANCE == null) { // Check 1
            synchronized (Foo.class) {
                if (INSTANCE == null) { // Check 2
                    INSTANCE = new Foo();
                }
            }
        }
        return INSTANCE;
    }
}

이것을 "이중 확인 잠금 관용구"라고합니다. 변덕스러운 진술을 잊기 쉽고 그것이 왜 필요한지 이해하기 어렵다.
자세한 내용은 http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

이제 우리는 사악한 스레드에 대해 확신하지만 잔인한 직렬화는 어떻습니까? de-serialization이 새 개체를 만들지 않아도 확인해야합니다.

class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static volatile Foo INSTANCE = null;

    // Rest of the things are same as above

    // No more fear of serialization
    @SuppressWarnings("unused")
    private Object readResolve() {
        return INSTANCE;
    }
}

이 메소드 readResolve()는 객체가 이전 프로그램 실행에서 직렬화 된 경우에도 유일한 인스턴스가 반환되도록합니다.

마지막으로 스레드와 직렬화에 대한 충분한 보호 기능을 추가했지만 코드가 거대하고보기 흉해 보입니다. 우리의 영웅에게 화장을 줄 수 있습니다

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    // Wrapped in a inner static class so that loaded only when required
    private static class FooLoader {

        // And no more fear of threads
        private static final Foo INSTANCE = new Foo();
    }

    // TODO add private shouting construcor

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    // Damn you serialization
    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

예이 바로 우리 같은 영웅입니다 :)
라인이 있기 때문에 private static final Foo INSTANCE = new Foo();클래스가 경우에만 실행됩니다 FooLoader실제로 사용이 게으른 인스턴스를 돌봐,

스레드 안전성을 보장합니다.

그리고 우리는 지금까지 왔으며, 우리가 한 모든 것을 성취하는 가장 좋은 방법은 최선의 방법입니다

 public enum Foo {
       INSTANCE;
   }

내부적으로 어떤 것이 취급 될 것인가

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();
}

그게 다야! 더 이상 직렬화, 스레드 및 못생긴 코드에 대한 두려움이 없습니다. 또한 ENUMS 싱글 톤은 느리게 초기화 됩니다.

이 방법은보다 간결하고 직렬화 기계를 무료로 제공하며 정교한 직렬화 또는 리플렉션 공격에도 불구하고 다중 인스턴스화에 대한 확실한 보장을 제공한다는 점을 제외하고는 공공 현장 접근 방식과 기능적으로 동일합니다. 이 방법은 아직 널리 채택되지 않았지만 단일 요소 열거 형 유형이 단일 톤을 구현하는 가장 좋은 방법입니다.

"유효한 자바"의 여호수아 블로흐

이제 왜 ENUMS가 Singleton을 구현하는 가장 좋은 방법으로 여겨지고 인내심을 가져 주셔서 감사합니다 :)
블로그 에서 업데이트했습니다 .


3
간단히 설명하면 : enum을 사용하여 구현 된 싱글 톤은 느리게 초기화됩니다. 자세한 내용은 여기 : stackoverflow.com/questions/16771373/…

2
좋은 대답입니다. 마지막으로 클론 메소드를 재정 의하여 예외를 발생시킵니다.
riship89

2
@ xyz 좋은 설명, 나는 정말 즐기고 아주 쉽게 배웠고 결코 이것을 잊지 않기를 바랍니다
Premraj

1
내가 스택 오버 플로우에서 가장 좋은 답변 중 하나입니다. 감사!
Shay Tsadok

1
입니다 싱글로 열거를 사용으로 직렬화 문제 : 모든 멤버 필드의 값이되어 있지 직렬화 때문에 복원되지 않습니다. Java Object Serialization Specification, 버전 6.0을 참조하십시오 . 또 다른 문제 : 버전 관리가 없음-모든 열거 형 유형의 고정이 serialVersionUID입니다 0L. 세 번째 문제점 : 사용자 정의 없음 : 직렬화 및 역 직렬화 중에 열거 유형으로 정의 된 클래스 별 writeObject, readObject, readObjectNoData, writeReplace 및 readResolve 메소드가 무시됩니다.
Basil Bourque

124

Stu Thompson이 게시 한 솔루션은 Java5.0 이상에서 유효합니다. 그러나 오류가 발생하기 쉽다고 생각하기 때문에 사용하지 않는 것이 좋습니다.

변덕스러운 진술을 잊기 쉽고 그것이 왜 필요한지 이해하기 어렵다. 휘발성이 없으면이 코드는 이중 점검 잠금 반 패턴으로 인해 더 이상 스레드로부터 안전하지 않습니다. 이에 대한 자세한 내용은 Java Concurrency in Practice의 16.2.4 단락을 참조하십시오 . 한마디로 :이 패턴 (Java5.0 이전 또는 휘발성 명령문이없는)은 잘못된 상태에있는 Bar 오브젝트에 대한 참조를 리턴 할 수 있습니다.

이 패턴은 성능 최적화를 위해 고안되었습니다. 그러나 이것은 더 이상 실제 관심사가 아닙니다. 다음의 게으른 초기화 코드는 빠르고 더 읽기 쉽습니다.

class Bar {
    private static class BarHolder {
        public static Bar bar = new Bar();
    }

    public static Bar getBar() {
        return BarHolder.bar;
    }
}

1
그럴 수 있지! 나는 휘발성에 익숙하고 사용 중입니다. 아, 그리고 JCiP를 응원하는 세 사람.
Stu Thompson

1
오, 이것은 분명히 FindBugz 명성의 William Pugh가 주장한 접근법입니다.
Stu Thompson

4
@Stu Effective Java (저작권 2001)의 첫 번째 판은 항목 48에 따라이 패턴을 자세히 설명합니다.
Pascal Thivent

9
@Bno : 생성자를 비공개로 만드는 것은 어떻습니까?
xyz

2
@ AlikElzin-kilaka 그렇지 않습니다. 인스턴스는 BarHolder 의 클래스 로딩 단계에서 작성되며 , 처음 필요할 때까지 지연됩니다. Bar의 생성자는 원하는만큼 복잡 할 수 있지만 첫 번째까지 호출되지는 않습니다 getBar(). (그리고 getBar"너무 이른"이라고 불린다면 싱글 온이 어떻게 구현 되든지 동일한 문제에 직면하게 될 것입니다.) 위의 코드가
느리게

95

Java 5 이상에서 스레드 안전 :

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar(); 
            }
        }
        return bar;
    }
}

편집 : volatile여기 수정 자에 주의 하십시오. :) 그렇지 않으면 JMM (Java Memory Model)에서 다른 스레드의 값 변경을 보증하지 않으므로 중요합니다. 동기화 는이 를 처리 하지 않으며 해당 코드 블록에 대한 액세스 만 직렬화합니다.

편집 2 : @Bno의 답변은 Bill Pugh (FindBugs)가 권장하는 접근 방식을 자세히 설명하며 논쟁의 여지가 있습니다. 가서 그의 답변도 투표하세요.


1
휘발성 수정 자에 대한 자세한 정보는 어디서 얻을 수 있습니까?
eleven81


2
반사 공격에 대해 언급하는 것이 중요하다고 생각합니다. 대부분의 개발자가 걱정할 필요는 없지만, Enum 기반 싱글 톤 이상의 이러한 예제에는 다중 인스턴스 공격으로부터 보호하거나 그러한 가능성을 나타내는 면책 조항을 두는 코드가 포함되어있는 것 같습니다.
luis.espinal

2
동기화는 상호 배제와 메모리 가시성을 모두 제공하므로 휘발성 키워드는 필요하지 않습니다.
Hemant

2
왜 Java 5 이상 에서이 모든 것을 귀찮게합니까? 열거 형 접근 방식은 스레드 안전성과 지연 초기화를 모두 제공한다는 것을 이해합니다. 또한 훨씬 간단합니다 ... 또한 열거 형을 피하려면 중첩 정적 클래스 접근을 여전히 막을 것입니다 ...
Alexandros

91

게으른 초기화를 잊어 버려 너무 문제가 많습니다. 이것이 가장 간단한 해결책입니다.

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}

21
싱글 톤 인스턴스 변수도 최종적으로 만들 수 있습니다. 예를 들어 private static final A singleton = new A ();
jatanp

19
정적 싱글 톤은 클래스가로드 될 때까지 인스턴스화되지 않고 클래스가 필요할 때까지로드되지 않기 때문에 효과적으로 게으른 초기화가 아닙니다.
Dan Dyer

7
정적을 인스턴스화하기 전에 클래스 A가로드되면 정적 내부 클래스에서 정적을 랩하여 클래스 초기화를 분리 할 수 ​​있습니다.
Tom Hawtin-tackline

3
나는이 대답이 가장 간단하고 Anirudhan에 동의하며 인스턴스를 final로 선언 할 필요가 없습니다. 정적 멤버가 초기화되는 동안 다른 스레드는 클래스에 액세스 할 수 없습니다. 이것은 컴파일러에 의해 보장됩니다. 즉, 모든 정적 초기화는 하나의 스레드 만 동기화 된 방식으로 수행됩니다.
inor

4
이 접근 방식에는 한 가지 제한 사항이 있습니다. 생성자는 예외를 throw 할 수 없습니다.
wangf

47

실제로 필요한지 확인하십시오. 이에 대한 몇 가지 주장을 보려면 "단일 안티 패턴"에 대한 Google을 수행하십시오. 본질적으로 잘못된 점은 없지만 전 세계 리소스 / 데이터를 노출하는 메커니즘 일 뿐이므로 이것이 최선의 방법인지 확인하십시오. 특히 DI가 테스트 목적으로 조롱 된 리소스를 사용할 수 있기 때문에 단위 테스트를 사용하는 경우 특히 종속성 주입이 더 유용하다는 것을 알았습니다.


당신은 전통적인 방법으로 모의 값을 주입 할 수 있지만 표준 / srping 방법이 아니라고 생각합니다. 그래서 추가 작업은 레거시 코드
일뿐입니다

21

나는 싱글 톤을 사용하는 것에 대한 대안으로 DI를 제안하는 답변 중 일부에 대해 신비합니다. 이들은 관련이없는 개념입니다. DI를 사용하여 싱글 톤 또는 비 싱글 톤 (예 : 스레드 당) 인스턴스를 주입 할 수 있습니다. Spring 2.x를 사용하는 경우 적어도 이것이 사실입니다. 다른 DI 프레임 워크에 대해서는 말할 수 없습니다.

따라서 OP에 대한 나의 대답은 (가장 간단한 샘플 코드를 제외하고) 다음과 같습니다.

  1. Spring과 같은 DI 프레임 워크를 사용하고
  2. 종속성이 싱글 톤인지, 요청 범위, 세션 범위 등 무엇이든 DI 구성의 일부로 만드십시오.

이 접근 방식은 싱글 톤을 사용할지 여부를 쉽게 되돌릴 수있는 구현 세부 사항 (물론 스레드 안전성이 제공되는 경우)으로 분리 된 (따라서 유연하고 테스트 가능한) 아키텍처를 제공합니다.


4
사람들이 당신과 동의하지 않기 때문일 수 있습니다. 난 당신을 downvoted하지 않았지만, 나는 동의하지 않습니다 : 나는 DI가 싱글 톤과 같은 문제를 해결하는 데 사용될 수 있다고 생각합니다. 이것은 "단일 인스턴스"를 의미하는 것으로 "단일 인스턴스를 가진 개체"라기보다는 "글로벌 이름으로 직접 액세스하는 단일 인스턴스를 가진 개체"를 의미합니다.
Tom Anderson

2
약간 확장하려면 TicketNumberer단일 글로벌 인스턴스가 필요한 TicketIssuer코드와 코드 줄이 포함 된 클래스를 작성하려는 위치를 고려하십시오 int ticketNumber = ticketNumberer.nextTicketNumber();. 전통적인 싱글 톤 사고에서 이전 코드 줄은 다음과 같아야 TicketNumberer ticketNumberer = TicketNumberer.INSTANCE;합니다. DI 사고에서 클래스에는와 같은 생성자가 있습니다 public TicketIssuer(TicketNumberer ticketNumberer) { this.ticketNumberer = ticketNumberer; }.
Tom Anderson

2
그리고 그 생성자를 호출하는 것은 다른 사람의 문제가됩니다. DI 프레임 워크는 일종의 글로벌 맵으로 수행합니다. 앱의 main메소드 (또는 그 하수인 중 하나)가 종속성을 생성 한 다음 생성자를 호출 하기 때문에 수작업으로 제작 된 DI 아키텍처가이를 수행합니다 . 기본적으로 전역 변수 (또는 전역 방법)의 사용은 단순한 형태의 끔찍한 서비스 로케이터 패턴 이며, 해당 패턴의 다른 용도와 마찬가지로 의존성 주입으로 대체 될 수 있습니다.
Tom Anderson

@TomAnderson 사람들이 왜 서비스 로케이터 패턴을 '무서워'하는지 혼란스러워합니다. 나는 대부분의 경우 과잉이거나 기껏해야 필요하지 않다고 생각하지만 유용한 사례가 있습니다. 더 적은 수의 매개 변수를 사용하면 DI가 확실히 선호되지만 20+ 이상을 상상하십시오. 코드가 구조화되지 않았다고 말하는 것은 올바른 인수가 아닙니다. 때로는 매개 변수 그룹화가 의미가 없기 때문입니다. 또한 단위 테스트 관점에서 서비스 테스트, 비즈니스 로직 만 신경 쓰지 않고 코드가 올바르게 작성되면 쉽습니다. 나는 대규모 프로젝트에서만 이러한 필요성을 보았습니다.
Brandon Ling

20

작성하기 전에 왜 싱글 톤이 필요한지 실제로 고려하십시오. Java로 Google 싱글 톤을 사용하면 쉽게 넘어 질 수있는 그것들을 사용하는 것에 대한 준 종교적 논쟁이 있습니다.

개인적으로 나는 여러 가지 이유로 가능한 한 자주 싱글 톤을 피하려고 노력하며, 대부분은 인터넷 검색 싱글 톤으로 찾을 수 있습니다. 모든 사람들이 이해하기 쉽기 때문에 싱글 톤이 남용되는 경우가 많으며, "글로벌"데이터를 OO 디자인으로 가져 오는 메커니즘으로 사용되며 객체 수명주기 관리를 우회하기 쉽기 때문에 사용됩니다. 실제로 B 내부에서 A를 수행하는 방법에 대해 생각합니다. 중간 정도의 제어를 위해 IoC (Inversion of Control) 또는 DI (Dependency Injection)와 같은 것을 살펴보십시오.

정말로 필요한 경우 wikipedia에는 ​​싱글 톤의 적절한 구현에 대한 좋은 예가 있습니다.


동의했다. 응용 프로그램의 나머지 부분을 시작하는 기본 클래스에 가깝습니다. 복제 된 경우 완전한 혼란 (즉, 리소스에 대한 단일 액세스 또는 보안 적용)으로 끝납니다. 응용 프로그램 전체에 글로벌 데이터를 전달하는 것은 큰 결합 적신호입니다. 정말로 필요하다고 인정할 때 사용하십시오.
살바도르 발렌시아

16

다음은 3 가지 접근 방식입니다

1) 열거 형

/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
    INSTANCE;
}

2) 이중 잠금 / 게으른 로딩 확인

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private static volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public static DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

3) 정적 공장 방법

/**
* Singleton pattern example with static factory method
*/

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}

13

스프링 프레임 워크를 사용하여 싱글 톤을 관리합니다. 클래스의 "단일성"을 강요하지는 않지만 (여러 클래스 로더가 포함 된 경우 실제로는 수행 할 수 없음) 서로 다른 유형의 오브젝트를 작성하기 위해 서로 다른 팩토리를 빌드하고 구성하는 정말 쉬운 방법을 제공합니다.


11

Wikipedia에는 Java에서도 싱글 톤 예제 가 있습니다. Java 5 구현은 매우 완벽 해 보이며 스레드로부터 안전합니다 (더블 체크 잠금 적용).


11

버전 1 :

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

지연 로딩, 블로킹으로 스레드 안전, 성능 저하로 인한 성능 저하 synchronized.

버전 2 :

public class MySingleton {
    private MySingleton() {}
    private static class MySingletonHolder {
        public final static MySingleton instance = new MySingleton();
    }
    public static MySingleton getInstance() {
        return MySingletonHolder.instance;
    }
}

지연없는 로딩, 비 차단, 고성능 스레드 안전.


10

게으른 로딩이 필요하지 않으면 간단히 시도하십시오.

public class Singleton {
    private final static Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() { return Singleton.INSTANCE; }

    protected Object clone() {
        throw new CloneNotSupportedException();
    }
}

게으른 로딩을 원하고 Singleton을 스레드로부터 안전하게하려면 이중 확인 패턴을 사용해보십시오.

public class Singleton {
        private static Singleton instance = null;

        private Singleton() {}

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

        protected Object clone() {
            throw new CloneNotSupportedException();
        }
}

이중 검사 패턴이 작동하지 않을 수도 있으므로 (컴파일러의 일부 문제로 인해 더 이상 아무것도 모릅니다) 전체 getInstance 메소드를 동기화하거나 모든 싱글 톤에 대한 레지스트리를 만들 수도 있습니다.


2
첫 번째 버전이 가장 좋습니다. 클래스가 싱글 톤을 제공하는 것 외에는 아무것도하지 않는다고 가정하면 일반적으로 게으른 클래스 로딩으로 인해 두 번째 버전의 인스턴스와 거의 같은 시점에 인스턴스화됩니다.
Dan Dyer

1
정적에 대한 이중 점검은 의미가 없습니다. 그리고 왜 보호 된 복제 방법을 공개 했습니까?
Tom Hawtin-tackline

1
-1 이중 확인 잠금 버전이 손상되었습니다.
assylias

5
또한 싱글 톤 변수를 만들어야합니다volatile
MyTitle

첫 번째 버전 게으르고 스레드로부터 안전합니다.
Miha_x64

9

나는 Enum singleton이라고 말할 것입니다

Java에서 enum을 사용하는 싱글 톤은 일반적으로 enum 싱글 톤을 선언하는 방법입니다. 열거 형 싱글 톤은 인스턴스 변수와 인스턴스 메소드를 포함 할 수 있습니다. 간단하게하기 위해 인스턴스 메소드를 사용하는 경우 해당 메소드의 스레드 안전성을 보장해야하는 것보다 오브젝트의 상태에 영향을 미치는 경우가 있습니다.

열거 형을 사용하면 구현하기가 쉽고 직렬화 가능한 객체와 관련하여 단점이 없으며 다른 방법으로 우회해야합니다.

/**
* Singleton pattern example using Java Enum
*/
public enum Singleton {
        INSTANCE;
        public void execute (String arg) {
                //perform operation here
        }
}

Singleton에서 메소드를 Singleton.INSTANCE호출 getInstance()하는 것보다 훨씬 쉽게 액세스 할 수 있습니다 .

1.12 열거 형 상수의 직렬화

열거 형 상수는 일반 직렬화 가능 객체 또는 외부화 가능 객체와 다르게 직렬화됩니다. 열거 형 상수의 직렬화 된 형식은 이름으로 만 구성됩니다. 상수의 필드 값은 양식에 없습니다. 열거 형 상수를 직렬화하려면 열거 ObjectOutputStream형 상수의 이름 메소드에서 리턴 한 값을 씁니다. 열거 형 상수를 역 직렬화하려면 ObjectInputStream스트림에서 상수 이름을 읽습니다. 역 직렬화 된 상수는 java.lang.Enum.valueOf메소드 를 호출 하여 상수의 열거 형과 함께 수신 된 상수 이름을 인수로 전달하여 얻습니다 . 다른 직렬화 가능 객체 또는 외부화 가능 객체와 마찬가지로 열거 형 상수는 직렬화 스트림에 이후에 나타나는 역 참조 대상으로 작동 할 수 있습니다.

열거 상수가 직렬화하는 프로세스를 정의 할 수 없습니다 : 모든 클래스 별은 writeObject, readObject, readObjectNoData, writeReplace, 및 readResolveenum 형에 의해 정의 된 메소드는 직렬화 및 역 직렬화하는 동안 무시됩니다. 마찬가지로 모든 serialPersistentFields또는 serialVersionUID필드 선언도 무시됩니다. 모든 열거 형 유형은 고정 serialVersionUID입니다 0L. 전송 된 데이터 유형에 변동이 없으므로 열거 형 유형에 대한 직렬화 가능 필드 및 데이터를 문서화 할 필요가 없습니다.

오라클 문서에서 인용

기존의 싱글 톤의 또 다른 문제점은 Serializable인터페이스 를 구현 한 후에는 readObject()메소드가 항상 Java의 생성자와 같은 새 인스턴스를 리턴 하기 때문에 더 이상 싱글 톤으로 남아 있지 않다는 것 입니다. readResolve()아래와 같이 싱글 톤으로 교체하여 새로 생성 된 인스턴스를 사용 하고 삭제 하면이를 피할 수 있습니다.

 // readResolve to prevent another instance of Singleton
 private Object readResolve(){
     return INSTANCE;
 }

일시적으로 만들어야 할 때 싱글 톤 클래스가 상태를 유지하는 경우 훨씬 더 복잡해질 수 있지만 Enum Singleton에서는 직렬화가 JVM에 의해 보장됩니다.


잘 읽다

  1. 싱글 톤 패턴
  2. 열거 형, 싱글 톤 및 역 직렬화
  3. 이중 점검 잠금 및 싱글 톤 패턴

8
There are 4 ways to create a singleton in java.

1- eager initialization singleton

    public class Test{
        private static final Test test = new Test();
        private Test(){}
        public static Test getTest(){
            return test;
        }
    

2- lazy initialization singleton (thread safe)

    public class Test {
         private static volatile Test test;
         private Test(){}
         public static Test getTest() {
            if(test == null) {
                synchronized(Test.class) {
                    if(test == null){test = new Test();
                }
            }
         }

        return test;
    


3- Bill Pugh Singleton with Holder Pattern (Preferably the best one)

    public class Test {

        private Test(){}

        private static class TestHolder{
            private static final Test test = new Test();
        }

        public static Test getInstance(){
            return TestHolder.test;
        }
    }

4- enum singleton
      public enum MySingleton {
        INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}

(1) 열망하지 않고 JVM 클래스 로딩 메커니즘으로 인해 지연됩니다.
Miha_x64

@ Miha_x64 내가 열망하는 로딩을 말했을 때, 나는 열망하는 초기화라고 말했다.
Dheeraj Sachan

효과적인 Java는 훌륭한 책이지만 반드시 편집해야합니다.
Miha_x64

@ Miha_x64 열망하는 로딩, 예를 통해 설명해 주시겠습니까
Dheeraj Sachan

'열심히'무언가를하는 것은 '가능한 빨리'를 의미합니다. 예를 들어, Hibernate는 명시 적으로 필요한 경우 간결한로드 관계를 지원합니다.
Miha_x64

8

이것에 대해 게임에 약간 늦을 수도 있지만 싱글 톤을 구현하는 데 많은 뉘앙스가 있습니다. 홀더 패턴은 여러 상황에서 사용할 수 없습니다. 휘발성을 사용할 때 IMO-로컬 변수도 사용해야합니다. 처음부터 시작하여 문제를 반복합시다. 당신은 내가 무슨 뜻인지 알 수 있습니다.


첫 번째 시도는 다음과 같습니다.

public class MySingleton {

     private static MySingleton INSTANCE;

     public static MySingleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new MySingleton();
        }

        return INSTANCE;
    }
    ...
}

여기에는 INSTANCE라는 개인 정적 멤버와 getInstance ()라는 공용 정적 메서드가있는 MySingleton 클래스가 있습니다. getInstance ()가 처음 호출 될 때 INSTANCE 멤버는 널입니다. 그러면 플로우가 작성 조건에 들어가고 MySingleton 클래스의 새 인스턴스가 작성됩니다. 이후의 getInstance () 호출은 INSTANCE 변수가 이미 설정되어 있으므로 다른 MySingleton 인스턴스를 작성하지 않습니다. 이렇게하면 getInstance ()의 모든 호출자간에 공유되는 MySingleton 인스턴스가 하나만있게됩니다.

그러나이 구현에는 문제가 있습니다. 멀티 스레드 애플리케이션은 단일 인스턴스 생성시 경쟁 조건을 갖습니다. 여러 개의 실행 스레드가 동시에 getInstance () 메소드에 도달하면 각각 INSTANCE 멤버가 널로 표시됩니다. 이로 인해 각 스레드는 새 MySingleton 인스턴스를 작성하고 INSTANCE 멤버를 설정합니다.


private static MySingleton INSTANCE;

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

    return INSTANCE;
}

여기서는 메소드 서명에서 synchronized 키워드를 사용하여 getInstance () 메소드를 동기화했습니다. 이것은 확실히 우리의 경쟁 조건을 해결합니다. 스레드는 이제 한 번에 하나씩 메소드를 차단하고 입력합니다. 그러나 성능 문제도 발생합니다. 이 구현은 단일 인스턴스 작성을 동기화 할뿐만 아니라 읽기를 포함하여 getInstance ()에 대한 모든 호출을 동기화합니다. 읽기는 단순히 INSTANCE 값을 반환하기 때문에 동기화 할 필요가 없습니다. 읽기는 대부분의 호출을 구성하므로 (인스턴스화는 첫 번째 호출에서만 발생 함을 명심하십시오) 전체 메소드를 동기화하여 불필요한 성능 저하가 발생합니다.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronize(MySingleton.class) {
            INSTANCE = new MySingleton();
        }
    }

    return INSTANCE;
}

여기에서는 메소드 서명에서 MySingleton 인스턴스 작성을 래핑하는 동기화 된 블록으로 동기화를 이동했습니다. 그러나 이것이 우리의 문제를 해결합니까? 글쎄, 우리는 더 이상 읽기를 차단하지 않지만, 한 걸음 뒤로 물러났습니다. 여러 스레드가 동시에 또는 거의 동시에 getInstance () 메소드에 도달하고 모두 INSTANCE 멤버가 널로 표시됩니다. 그런 다음 동기화 된 블록에 도달하여 잠금을 획득하고 인스턴스를 작성합니다. 해당 스레드가 블록을 종료하면 다른 스레드가 잠금을 위해 경쟁하고 각 스레드가 하나씩 블록을 통과하여 클래스의 새 인스턴스를 만듭니다. 그래서 우리는 시작한 곳으로 돌아 왔습니다.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

여기서 우리는 블록 내부에서 또 다른 검사를 발행합니다. INSTANCE 멤버가 이미 설정된 경우 초기화를 건너 뜁니다. 이것을 이중 점검 잠금이라고합니다.

이를 통해 다중 인스턴스화 문제를 해결할 수 있습니다. 그러나 다시 한 번, 우리의 솔루션은 또 다른 도전을 제시했습니다. 다른 스레드는 INSTANCE 멤버가 업데이트되었음을 ​​"보지"않을 수 있습니다. 이것은 Java가 메모리 작업을 최적화하는 방법 때문입니다. 스레드는 변수의 원래 값을 기본 메모리에서 CPU의 캐시로 복사합니다. 그런 다음 값에 대한 변경 사항이 해당 캐시에 기록되고 해당 캐시에서 읽 힙니다. 이것은 성능을 최적화하도록 설계된 Java의 기능입니다. 그러나 이것은 싱글 톤 구현에 문제가됩니다. 다른 캐시를 사용하여 다른 CPU 또는 코어에서 처리중인 두 번째 스레드는 첫 번째 스레드의 변경 사항을 볼 수 없습니다. 그러면 두 번째 스레드가 INSTANCE 멤버를 null로 간주하여 싱글 톤의 새 인스턴스를 만듭니다.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

INSTANCE 멤버 선언에 volatile 키워드를 사용하여이 문제를 해결합니다. 이것은 컴파일러에게 CPU 캐시가 아닌 메인 메모리를 항상 읽고 쓰도록 지시합니다.

그러나이 간단한 변화는 비용이 듭니다. 우리는 CPU 캐시를 우회하기 때문에 휘발성 INSTANCE 멤버에서 작업 할 때마다 성능이 저하됩니다. 존재 (1과 2)를 다시 확인하고 값 (3)을 설정 한 다음 값 (4)을 반환합니다. 메소드의 첫 번째 호출 동안 인스턴스를 작성하기 때문에이 경로가 프린지 케이스라고 주장 할 수 있습니다. 아마도 창조에 대한 성능 저하는 용납 될 수 있습니다. 그러나 우리의 주요 사용 사례 인 읽기조차도 휘발성 멤버에서 두 번 작동합니다. 한 번 존재를 확인하고 다시 값을 반환합니다.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    MySingleton result = INSTANCE;
    if (result == null) {
        synchronized(MySingleton.class) {
            result = INSTANCE;
            if (result == null) {
                INSTANCE = result = createInstance();
            }
        }
    }

    return result;
}

성능 적중은 휘발성 멤버에서 직접 작동하기 때문에 로컬 변수를 휘발성 값으로 설정하고 대신 로컬 변수에서 작동합시다. 이렇게하면 휘발성에 대한 작업 횟수가 줄어들어 손실 된 성능 중 일부가 회복됩니다. 동기화 된 블록에 들어갈 때 로컬 변수를 다시 설정해야합니다. 이렇게하면 잠금을 기다리는 동안 발생한 모든 변경 사항이 최신 상태로 유지됩니다.

최근에 이것에 관한 기사를 썼습니다. 싱글 톤 해체 . 이 예에 대한 자세한 정보와 "홀더"패턴의 예를 찾을 수 있습니다. 이중 점검 휘발성 접근 방식을 보여주는 실제 사례도 있습니다. 도움이 되었기를 바랍니다.


기사BearerToken instance 에서 왜 그렇지 않은지 설명해 주 시겠습니까? 그리고 그것은 무엇입니까? staticresult.hasExpired()
Woland

그리고 class MySingleton어쩌면 – 아마도 그럴까요 final?
Woland

1
@Woland BearerToken인스턴스가 BearerTokenFactory 특정 권한 서버로 구성된-의 일부이므로 정적이 아닙니다 . 많은 BearerTokenFactory개체 가있을 수 있습니다. 각 개체 에는 자체 "캐시 된"개체 BearerToken가 있으며 만료 될 때까지 배포 할 수 있습니다. 의 hasExpired()메소드 는 만료 된 토큰을 전달하지 않도록 BeraerToken팩토리 get()메소드 에서 호출됩니다 . 만료되면 인증 서버에서 새 토큰이 요청됩니다. 코드 블록 다음에 나오는 단락에서이를 자세히 설명합니다.
Michael Andrews

4

다음은 간단한 구현 방법입니다 singleton.

public class Singleton {
    // It must be static and final to prevent later modification
    private static final Singleton INSTANCE = new Singleton();
    /** The constructor must be private to prevent external instantiation */ 
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

이것은 게으른 게을 만드는 방법입니다 singleton:

public class Singleton {
    // The constructor must be private to prevent external instantiation   
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    /** 
     * The static inner class responsible for creating your instance only on demand,
     * because the static fields of a class are only initialized when the class
     * is explicitly called and a class initialization is synchronized such that only 
     * one thread can perform it, this rule is also applicable to inner static class
     * So here INSTANCE will be created only when SingletonHolder.INSTANCE 
     * will be called
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

싱글 톤에서 필요한 유일한 것은 인스턴스라고 가정하면 둘 다 게으르다.
Miha_x64

@ Miha_x64 첫 번째 경우는 JVM이 클래스를 초기화 할 때 싱글 톤을 인스턴스화하고, 두 번째 경우는를 호출 할 때 싱글 톤을 인스턴스화합니다 getInstance(). 그러나 실제로 클래스에 다른 정적 메소드 가없고 Singleton호출 getInstance()만하면 실제 차이는 없습니다.
Nicolas Filotto

3

당신은 필요가 두 번 확인 관용구를 사용하면 게으르게 클래스의 인스턴스 변수를로드해야합니다. 정적 변수 또는 싱글 톤을 느리게로드해야하는 경우 주문형 홀더 관용구를 초기화 해야합니다 .

또한 싱글 톤이 서비스 가능해야하는 경우 다른 모든 필드는 일시적이어야하며 싱글 톤 객체를 불변으로 유지하려면 readResolve () 메소드를 구현해야합니다. 그렇지 않으면 객체가 직렬화 해제 될 때마다 객체의 새 인스턴스가 작성됩니다. readResolve ()가하는 것은 readObject ()가 읽은 새로운 객체를 대체하는 것인데,이 객체를 참조하는 변수가 없기 때문에 새로운 객체를 가비지 수집해야합니다.

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // original singleton instance.
} 

3

싱글 톤 객체를 만드는 다양한 방법 :

  1. Joshua Bloch에 따르면 Enum이 가장 좋습니다.

  2. 이중 점검 잠금도 사용할 수 있습니다.

  3. 내부 정적 클래스도 사용할 수 있습니다.


3

열거 형 싱글 톤

스레드로부터 안전한 싱글 톤을 구현하는 가장 간단한 방법은 Enum을 사용하는 것입니다

public enum SingletonEnum {
  INSTANCE;
  public void doSomething(){
    System.out.println("This is a singleton");
  }
}

이 코드는 Java 1.5에서의 Enum 도입 이후 작동합니다.

이중 점검 잠금

멀티 스레드 환경 (Java 1.5에서 시작)에서 작동하는 "클래식"싱글 톤을 코딩하려면이 것을 사용해야합니다.

public class Singleton {

  private static volatile Singleton instance = null;

  private Singleton() {
  }

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

volatile 키워드의 구현이 다르기 때문에 1.5 이전의 스레드로부터 안전하지 않습니다.

싱글 톤 조기로드 (Java 1.5 이전에도 작동)

이 구현은 클래스가로드 될 때 싱글 톤을 인스턴스화하고 스레드 안전성을 제공합니다.

public class Singleton {

  private static final Singleton instance = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return instance;
  }

  public void doSomething(){
    System.out.println("This is a singleton");
  }

}

2

JSE 5.0 이상에서는 Enum 방식을 사용하고, 그렇지 않으면 정적 싱글 톤 홀더 방식 (Bill Pugh가 설명하는 게으른 로딩 방식)을 사용하십시오. Later 솔루션은 특수 언어 구성 (예 : 휘발성 또는 동기화)이 없어도 스레드로부터 안전합니다.


2

싱글 톤에 대해 종종 사용되는 또 다른 주장은 테스트 가능성 문제입니다. 싱글 톤은 테스트 목적으로 쉽게 조롱 할 수 없습니다. 이것이 문제로 판명되면 다음과 같이 약간 수정하고 싶습니다.

public class SingletonImpl {

    private static SingletonImpl instance;

    public static SingletonImpl getInstance() {
        if (instance == null) {
            instance = new SingletonImpl();
        }
        return instance;
    }

    public static void setInstance(SingletonImpl impl) {
        instance = impl;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

추가 된 setInstance메소드를 사용하면 테스트 중에 싱글 톤 클래스의 모형 구현을 설정할 수 있습니다.

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

이것은 초기 초기화 접근 방식에서도 작동합니다.

public class SingletonImpl {

    private static final SingletonImpl instance = new SingletonImpl();

    private static SingletonImpl alt;

    public static void setInstance(SingletonImpl inst) {
        alt = inst;
    }

    public static SingletonImpl getInstance() {
        if (alt != null) {
            return alt;
        }
        return instance;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

이것은이 기능을 일반 응용 프로그램에 노출시키는 단점도 있습니다. 해당 코드를 작업하는 다른 개발자는 'setInstance'메소드를 사용하여 특정 기능을 변경하여 전체 애플리케이션 동작을 변경하려는 유혹을받을 수 있으므로이 메소드에는 적어도 javadoc에 경고가 포함되어야합니다.

여전히 모형 테스트 (필요한 경우)의 가능성을 위해이 코드 노출은 허용되는 가격이 될 수 있습니다.


1

가장 간단한 싱글턴 클래스

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}

1
이것은 아래의 Jonathan의 답변과 동일합니다
inor

1
Jonathan 이이 형제 자매 답변 을 중복하여 5 년 전에 게시했습니다. 흥미로운 의견은 해당 답변을 참조하십시오.
Basil Bourque

0

Java 1.5 이후에도 enum은 멀티 스레드 환경에서도 하나의 인스턴스 만 생성되도록 보장하므로 사용 가능한 최상의 싱글 톤 구현입니다.

public enum Singleton{ INSTANCE; }

그리고 당신은 끝났습니다!


1
이것은 이미 몇 년 전에 다른 답변에서 언급되었습니다.
Pang

0

이 게시물을 살펴보십시오.

Java 핵심 라이브러리의 GoF 디자인 패턴 예

가장 좋은 답변의 "Singleton"섹션에서

싱글 톤 (매번 동일한 인스턴스 (보통 자체)을 리턴하는 작성 메소드로 인식 가능)

  • java.lang.Runtime # getRuntime ()
  • java.awt.Desktop # getDesktop ()
  • java.lang.System # getSecurityManager ()

Java 네이티브 클래스 자체에서 Singleton의 예를 배울 수도 있습니다.


0

내가 본 최고의 싱글 톤 패턴은 공급 업체 인터페이스를 사용합니다.

  • 일반적이며 재사용 가능
  • 게으른 초기화를 지원합니다
  • 초기화 될 때까지만 동기화 된 다음 차단 공급 업체가 비 차단 공급 업체로 교체됩니다.

아래를보십시오 :

public class Singleton<T> implements Supplier<T> {

    private boolean initialized;
    private Supplier<T> singletonSupplier;

    public Singleton(T singletonValue) {
        this.singletonSupplier = () -> singletonValue;
    }

    public Singleton(Supplier<T> supplier) {
        this.singletonSupplier = () -> {
            // The initial supplier is temporary; it will be replaced after initialization
            synchronized (supplier) {
                if (!initialized) {
                    T singletonValue = supplier.get();
                    // Now that the singleton value has been initialized,
                    // replace the blocking supplier with a non-blocking supplier
                    singletonSupplier = () -> singletonValue;
                    initialized = true;
                }
                return singletonSupplier.get();
            }
        };
    }

    @Override
    public T get() {
        return singletonSupplier.get();
    }
}

-3

때로는 간단한 " static Foo foo = new Foo();" 로는 충분하지 않습니다. 당신이하고 싶은 기본적인 데이터 삽입을 생각하십시오.

반면에 싱글 톤 변수를 인스턴스화하는 모든 메소드를 동기화해야합니다. 동기화는 그렇게 나쁘지는 않지만 성능 문제 또는 잠금을 유발할 수 있습니다 (이 예를 사용하는 매우 드문 상황).

public class Singleton {

    private static Singleton instance = null;

    static {
          instance = new Singleton();
          // do some of your instantiation stuff here
    }

    private Singleton() {
          if(instance!=null) {
                  throw new ErrorYouWant("Singleton double-instantiation, should never happen!");
          }
    }

    public static getSingleton() {
          return instance;
    }

}

이제 어떻게됩니까? 클래스는 클래스 로더를 통해로드됩니다. 바이트 배열에서 클래스가 해석 된 직후 VM은 정적 {} -블록을 실행합니다 . 그것은 비밀입니다. 정적 블록은 한 번만 호출되며, 주어진 패키지의 주어진 클래스 (이름)가이 하나의 클래스 로더에 의해로드 된 시간입니다.


3
사실이 아니다. 정적 변수는 클래스가로드 될 때 정적 블록과 함께 초기화됩니다. 선언을 나눌 필요가 없습니다.
Craig P. Motlin

-5
public class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton(){
    if (INSTANCE != null)
        throw new IllegalStateException (“Already instantiated...”);
}

    public synchronized static Singleton getInstance() { 
    return INSTANCE;

    }

}

getInstance 앞에 Synchronized 키워드를 추가 했으므로 두 스레드가 동시에 getInstance를 호출하는 경우 경쟁 조건을 피했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.