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




예제 유니티 (25)

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


Answers

그것을 작성하기 전에 싱글 톤이 필요한 이유를 정말로 고려하십시오. Java를 사용하여 Google 싱글 톤을 사용하면 쉽게 우연히 발견 할 수있는 준 종교적 논쟁이 있습니다.

개인적으로 나는 싱글 톤을 가능한 한 자주 여러 가지 이유로 피하려고 노력합니다. 싱글 톤은 대부분 싱글 톤으로 검색 할 수 있습니다. 싱글 톤은 모두가 쉽게 이해할 수 있기 때문에 학대 당하고 있다고 생각합니다. OO 디자인에 "글로벌"데이터를 가져 오는 메커니즘으로 사용되며, 객체 라이프 사이클 관리를 회피하기 쉽기 때문에 사용됩니다 (또는 B에서 내부에서 어떻게 할 수 있는지에 대해 정말로 생각해보십시오.) Inversion of Control (IoC) 또는 Dependency Injection (DI)과 같은 멋진 중간계를 살펴보십시오.

당신이 정말로 하나를 필요로한다면 위키 피 디아는 싱글 톤의 적절한 구현의 좋은 예가됩니다.


당신이해야 할 double-checking 는 게으르게 클래스의 인스턴스 변수를로드해야하는 경우 관용구. 정적 변수 또는 싱글 톤 지연을로드해야하는 경우 주문형 보유자 관용구 초기화 가 필요합니다 .

또한, 싱글 톤을 필요로하는 경우, 다른 모든 필드는 일시적 일 필요가 있고 싱글 톤 객체를 불변으로 유지하기 위해 readResolve () 메소드를 구현해야합니다. 그렇지 않으면 개체가 deserialize 될 때마다 개체의 새 인스턴스가 만들어집니다. readResolve ()가 수행하는 작업은 readObject ()가 읽는 새 객체를 대체하는 것으로,이 객체를 참조하는 변수가 없기 때문에 새 객체를 가비지 수집해야했습니다.

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

이 게시물을보십시오.

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

가장 좋은 답변의 "싱글 톤"섹션에서,

싱글 톤 (매번 같은 인스턴스를 반환하는 생성 메소드에서 인식 가능)

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

Java 원시 클래스 자체에서 Singleton의 예제를 배울 수도 있습니다.


다음 세 가지 접근법이 있습니다.

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;
    }
}

나는 여전히 Java 1.5 이후에 enum이 멀티 스레드 환경에서도 보장된다는 점에서 사용 가능한 최상의 싱글 톤 구현이라고 생각합니다. 단 하나의 인스턴스 만 생성됩니다.

public enum Singleton{ INSTANCE; }

그리고 너 끝났어!


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

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 키워드를 사용하여 사용자에게 final 키워드임을 알립니다. 그런 다음 사용자가 자신의 Foo를 만들지 못하도록 생성자를 비공개로 만들 필요가 있습니다. 생성자에서 예외를 throw하면 사용자가 리플렉션을 사용하여 두 번째 Foo를 만드는 것을 방지 할 수 있습니다. 그런 다음, 유일한 인스턴스를 보유하는 private static final Foo 필드를 작성하고이를 리턴하는 public static Foo getInstance() 메소드를 작성합니다. Java 스펙은 클래스가 처음 사용될 때만 생성자가 호출되도록합니다.

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

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가 실제로 사용될 때만 실행됩니다. 이것은 게으른 인스턴스화를 처리하고 스레드로부터 안전하다는 것을 보장합니다.

개체를 serialize 할 수도있게하려면 deserialization에서 복사본을 만들지 않아야합니다.

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() 는 객체가 이전 프로그램 실행에서 직렬화 된 경우에도 유일한 인스턴스가 리턴 될 것임을 확인합니다.


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의 대답은 빌 Pugh (FindBugs)가 권장하는 접근 방식으로 자세히 논증 할 수 있습니다. 가서 그의 대답에 투표하십시오.


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

휘발성 선언문을 잊어 버리고 필요한 이유를 이해하기 어렵습니다. 휘발성이 없으면이 코드는 double-checked locking antipattern 때문에 더 이상 스레드 안전하지 않습니다. 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;
    }
}

때때로 간단한 " 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은 정적 {} 블록을 실행합니다 . 그것은 전체 비밀입니다 : 정적 블록은 주어진 패키지의 주어진 클래스 (이름)가이 클래스 로더에 의해로드 된 시간에 한 번만 호출됩니다.


싱글 톤은 클래스 로더가로드 한 싱글 톤임을 잊지 마십시오. 여러 로더 (컨테이너)를 사용하는 경우 각각은 고유 한 버전의 Singleton을 가질 수 있습니다.


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

싱글 톤을 구현하는 동안 우리는 2 가지 옵션을가집니다.
1. 게으른 로딩
2. 조기 장전

게으른 로딩은 비트 오버 헤드를 추가하므로 (매우 정직해야 함) 매우 큰 객체 또는 무거운 코드가 있고 인스턴스가 필요하기 전에 사용할 수있는 다른 액세스 가능한 정적 메서드 나 필드가있는 경우에만 사용하십시오. 당신은 게으른 초기화를 사용해야합니다. 그렇지 않으면 초기로드를 선택하는 것이 좋습니다.

Singleton을 구현하는 가장 간단한 방법은 다음과 같습니다.

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;
    }
}

이것은 "Double-Checked Locking Idiom"이라고합니다. 휘발성 선언문을 잊어 버리고 필요한 이유를 이해하기 어렵습니다.
자세한 내용은 http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

이제 우리는 악의 스레드에 대해 확신하지만 잔인한 직렬화는 어떨까요? 비 직렬화가 새로운 객체가 생성되지 않는 동안에도 반드시 확인해야합니다.

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 클래스가 실제로 사용될 때만 실행되며, 이것은 게으른 인스턴스 생성,

thread 세이프 인 것을 보증합니다.

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

 public enum Foo {
       INSTANCE;
   }

내부적으로 어떤 대우를받을 것인가?

public class Foo {

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

그것은 직렬화, 스레드 및 추악한 코드에 대한 두려움이 아닙니다. 또한 ENUMS 싱글 톤은 지연 초기화 됩니다.

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

- "효과적인 자바"의 Joshua Bloch

이제 왜 ENUMS가 Singleton을 구현하고 인내심을 가져 주셔서 감사합니다.
blog 에서 업데이트했습니다.


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

Java에서 enum을 사용하는 싱글 톤은 일반적으로 enum singleton을 선언하는 방법입니다. Enum 싱글 톤에는 인스턴스 변수 및 인스턴스 메서드가 포함될 수 있습니다. 간단하게하기 위해 인스턴스 메서드를 사용하는 경우 인스턴스 메서드를 사용하는 경우 해당 메서드의 스레드 안전성을 보장해야한다는 것보다 객체의 상태에 영향을 주어야합니다.

열거 형의 사용은 구현하기가 매우 쉽고 다른 방법으로 우회되어야하는 직렬화 가능 객체에 대한 단점이 없습니다.

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

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

1.12 열거 형 상수의 직렬화

Enum 정수는 통상의 직렬화 가능 오브젝트 또는 외부화 가능 오브젝트와는 다른 방법으로 직렬화됩니다. 열거 형 정수의 직렬화 된 형식은, 이름만으로 구성됩니다. 상수의 필드 값이 양식에 없습니다. enum 정수를 직렬화하기 위해서 (때문에), ObjectOutputStream 는 enum 정수의 이름 메소드가 돌려주는 값을 ObjectOutputStream 합니다. enum 정수를 직렬화 복원하려면, ObjectInputStream 는 스트림으로부터 정수의 이름을 읽어들입니다. 비 직렬화 정수는, java.lang.Enum.valueOf 메소드를 호출 해, 인수의 정수로서 수신 한 정수의 이름과 함께 정수의 열거 형을 건네주는 것으로 취득 할 수 있습니다. 다른 직렬화 가능 객체 또는 외부화 가능 객체와 마찬가지로 열거 형 상수는 순차 화 스트림에 연속적으로 나타나는 역 참조의 대상으로 작동 할 수 있습니다.

열거 형 정수를 직렬화하는 프로세스는 커스터마이즈 할 수 없습니다. 열거 형에 의해 정의 된 클래스 고유의 writeObject , readObject , readObjectNoData , writeReplacereadResolve 메서드는 직렬화 및 직렬화 복원 중에는 무시됩니다. 마찬가지로 모든 serialPersistentFields 또는 serialVersionUID 필드 선언도 무시됩니다. 모든 enum 유형의 고정 serialVersionUID0L 입니다. 전송할 데이터 유형에 변화가 없으므로 직렬화 가능 필드 및 열거 형 데이터를 문서화하는 것은 불필요합니다.

오라클 문서에서 인용

재래식 싱글 톤의 또 다른 문제점은 일단 Serializable 인터페이스를 구현하면 readObject() 메소드가 Java에서 생성자와 같은 새로운 인스턴스를 항상 리턴하기 때문에 더 이상 Singleton으로 남아 있지 않다는 것입니다. 이것은 readResolve() 하고 아래처럼 싱글 톤으로 대체하여 새로 생성 된 인스턴스를 버림으로써 피할 수 있습니다

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

싱글 톤 클래스가 상태를 유지하는 경우 일시적으로 처리해야하므로이 작업은 더욱 복잡해질 수 있지만 Enum Singleton에서는 JVM에서 직렬화를 보장합니다.

좋은 읽기

  1. 싱글 톤 패턴
  2. 열거 형, 싱글 톤 및 비 직렬화
  3. 더블 체크 잠금 및 싱글 톤 패턴

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

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();
    }
}

게으른 로딩을 원하고 싱글 톤이 쓰레드에 안전하도록하려면 더블 체크 패턴을 사용하십시오

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-method를 동기화하거나 모든 Singleton에 대한 레지스트리를 만들려고 할 수도 있습니다.


열거 형 싱글 톤

thread-safe 인 Singleton을 구현하는 가장 간단한 방법은 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 ;
  }
}

이것은 휘발성 키워드의 구현이 다르기 때문에 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");
  }

}

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를 호출하는 경우 경쟁 조건을 피할 수 있습니다.


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

그래서 OP에 대한 내 대답은 (모든 사소한 샘플 코드에서) 다음과 같습니다.

  1. Spring과 같은 DI 프레임 워크를 사용하십시오.
  2. 의존성이 싱글 톤 (singleton), 범위 요구 (scope scoped), 세션 범위 (session scoped) 등 무엇이든 상관없이 DI 구성에 포함 시키십시오.

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


Wikipedia는 자바에서도 싱글 톤의 examples 줍니다. Java 5 구현은 꽤 완벽 해 보이며 스레드로부터 안전합니다 (이중 선택 잠금이 적용됨).


게으른 초기화는 잊어 버리십시오. 문제가 너무 많습니다. 이것은 가장 간단한 솔루션입니다.

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

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

열거 형을 사용하십시오.

public enum Foo {
    INSTANCE;
}

조슈아 블로흐 (Joshua Bloch)는 Google I / O 2008 : 비디오 링크의 Effective 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));
    }
}

편집 : "효과적인 자바"온라인 부분은 말합니다 :

"이 접근법은 공용 필드 접근법과 기능적으로 동일 합니다만, 더 간결하고 직렬화 기계를 무료로 제공하며 정교한 직렬화 또는 반사 공격에도 불구하고 다중 인스턴스화에 대한 철저한 보장을 제공합니다. 아직 널리 채택되기 위해서는 단일 요소 열거 형이 싱글 톤을 구현하는 가장 좋은 방법 입니다. "


내 싱글 톤을 관리하기 위해 스프링 프레임 워크를 사용한다. 클래스의 "단일성"을 강요하지는 않지만 (여러 클래스 로더가 관련된 경우에는 실제로 할 수 없습니다) 다양한 유형의 객체를 생성하기위한 여러 팩토리를 빌드하고 구성하는 정말 쉬운 방법을 제공합니다.


버전 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;
    }
}

게으른 로딩, 비 차단 및 고성능 스레드로 안전합니다.


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");
    }
}

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


가장 단순한 싱글 톤 클래스

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

더 똑똑한 :

for (String key : hashMap.keySet()) {
    System.out.println("Key: " + key + ", Value: " + map.get(key));
}




java singleton design-patterns