자바 싱글 톤 및 동기화


117

싱글 톤 및 멀티 스레딩에 대한 내 질문을 명확히하십시오.

  • 다중 스레드 환경에서 Java로 Singleton을 구현하는 가장 좋은 방법은 무엇입니까?
  • 여러 스레드 getInstance() 가 동시에 메서드에 액세스하려고하면 어떻게됩니까 ?
  • 싱글 톤을 만들 수 있습니까 getInstance() synchronized?
  • Singleton 클래스를 사용할 때 동기화가 정말로 필요합니까?

답변:


211

네, 필요합니다. 지연 초기화로 스레드 안전성을 달성하는 데 사용할 수있는 몇 가지 방법이 있습니다.

드라코 니안 동기화 :

private static YourObject instance;

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

이 솔루션을 사용하려면 실제로 처음 몇 개만 동기화 해야 할 때 모든 스레드를 동기화해야합니다.

동기화 재확인 :

private static final Object lock = new Object();
private static volatile YourObject instance;

public static YourObject getInstance() {
    YourObject r = instance;
    if (r == null) {
        synchronized (lock) {    // While we were waiting for the lock, another 
            r = instance;        // thread may have instantiated the object.
            if (r == null) {  
                r = new YourObject();
                instance = r;
            }
        }
    }
    return r;
}

이 솔루션은 싱글 톤을 획득하려는 처음 몇 개의 스레드 만 잠금 획득 프로세스를 거치도록합니다.

요청시 초기화 :

private static class InstanceHolder {
    private static final YourObject instance = new YourObject();
}

public static YourObject getInstance() {
    return InstanceHolder.instance;
}

이 솔루션은 스레드 안전성을 보장하기 위해 클래스 초기화에 대한 Java 메모리 모델의 보장을 활용합니다. 각 클래스는 한 번만로드 할 수 있으며 필요할 때만로드됩니다. 즉, 처음 getInstance호출되고 InstanceHolder로드되고 instance생성되며 ClassLoaders에 의해 제어 되므로 추가 동기화가 필요하지 않습니다.


23
경고-다시 확인 된 동기화에주의하십시오. 메모리 모델의 "문제"로 인해 Java 5 이전 JVM에서는 제대로 작동하지 않습니다.
스티븐 C

3
-1 Draconian synchronizationDouble check synchronizationgetInstance () 메서드는 정적이어야합니다!
저승

2
@PeterRader 그들은 그럴 필요static없지만 그들이 있다면 더 의미가있을 수 있습니다. 요청에 따라 수정되었습니다.
Jeffrey

4
이중 확인 잠금의 구현은 작동이 보장되지 않습니다. 실제로 이중 확인 잠금에 대해 인용 한 기사에 설명되어 있습니다. :) 1.5 이상에서 제대로 작동하는 휘발성을 사용하는 예가 있습니다 (이중 확인 된 잠금은 1.5 이하에서 일반 손상됨). 기사에서 인용 된 주문형 초기화 홀더는 아마도 답변에서 더 간단한 솔루션 일 것입니다.
stuckj

2
@MediumOne AFAIK r는 정확성을 위해 필요하지 않습니다. 지역 변수에 액세스하는 것보다 훨씬 비싸기 때문에 휘발성 필드에 액세스하는 것을 피하는 것은 최적화 일뿐입니다.
Jeffrey

69

이 패턴은 명시 적 동기화 없이 인스턴스의 스레드로부터 안전한 지연 초기화를 수행합니다 !

public class MySingleton {

     private static class Loader {
         static final MySingleton INSTANCE = new MySingleton();
     }

     private MySingleton () {}

     public static MySingleton getInstance() {
         return Loader.INSTANCE;
     }
}

클래스 로더를 사용하여 모든 동기화를 무료로 수행하기 때문에 작동합니다. 클래스 MySingleton.LoadergetInstance()메서드 내에서 먼저 액세스 되므로 처음 호출 Loader될 때 클래스가로드됩니다 getInstance(). 또한 클래스 로더는 클래스에 액세스하기 전에 모든 정적 초기화가 완료되도록 보장합니다. 이것이 스레드 안전성을 제공하는 것입니다.

마법 같아요.

실제로 Jhurtado의 enum 패턴과 매우 유사하지만 enum 패턴이 enum 개념의 남용이라는 것을 알았습니다 (작동하지만)


11
동기화는 여전히 존재하며 프로그래머가 아닌 JVM에 의해 강제됩니다.
Jeffrey

@Jeffrey 당연히 맞습니다-모든 것을 입력했습니다 (편집 참조)
Bohemian

2
나는 그것이 JVM에 아무런 차이가 없다는 것을 이해하고, 자체 문서화 된 코드가 진행되는 한 나에게 차이를 만들었다 고 말하고 있습니다. 나는 "final"키워드없이 자바에서 모든 대문자를 본 적이 없다. (또는 enum), 약간의인지 부조화가 있었다. Java를 풀 타임으로 프로그래밍하는 사람에게는 차이가 없을 수 있지만 언어를 앞뒤로 건너 뛰면 명시적인 것이 도움이됩니다. 초보자도 마찬가지입니다. 하지만이 스타일에 꽤 빨리 적응할 수있을 것입니다. 모든 대문자로 충분할 것입니다. nit pick을 의미하지 마십시오. 귀하의 게시물이 마음에 들었습니다.
Ruby

1
나는 그것의 일부를 얻지 못했지만 훌륭한 대답입니다. "게다가 클래스 로더는 클래스에 액세스하기 전에 모든 정적 초기화가 완료되도록 보장합니다. 이것이 스레드 안전성을 제공하는 것입니다." , 이것이 스레드 안전성에 어떻게 도움이되는지에 대해 약간 혼란 스럽습니다.
gaurav jain 2014

2
@ wz366 실제로는 필수는 아니지만 스타일상의 이유로 동의합니다 (다른 코드가 액세스 할 수 없기 때문에 사실상 최종적이기 때문에) final. 끝난.
보헤미안

21

Java의 다중 스레드 환경에서 작업하고 모든 스레드가 클래스의 단일 인스턴스에 액세스하도록 보장해야하는 경우 Enum을 사용할 수 있습니다. 이렇게하면 직렬화를 처리하는 데 도움이되는 추가 이점이 있습니다.

public enum Singleton {
    SINGLE;
    public void myMethod(){  
    }
}

그런 다음 스레드가 다음과 같이 인스턴스를 사용하도록합니다.

Singleton.SINGLE.myMethod();

8

예, getInstance()동기화 해야 합니다. 그렇지 않은 경우 클래스의 여러 인스턴스를 만들 수있는 상황이 발생할 수 있습니다.

getInstance()동시에 호출하는 두 개의 스레드가있는 경우를 고려하십시오 . 이제 T1이 instance == null검사를 통과 한 다음 T2가 실행된다고 상상해보십시오 . 이 시점에서 인스턴스가 생성되거나 설정되지 않았으므로 T2가 검사를 통과하고 인스턴스를 생성합니다. 이제 실행이 T1로 다시 전환된다고 상상해보십시오. 이제 싱글 톤이 생성되었지만 T1은 이미 확인을 완료했습니다! 다시 개체 만들기를 진행합니다! 제작 getInstance()이 문제 동기화 방지합니다.

싱글 톤을 스레드로부터 안전하게 만드는 몇 가지 방법이 있지만 getInstance()동기화하는 것이 아마도 가장 간단 할 것입니다.


전체 메서드를 동기화하는 대신 동기화 된 블록에 개체 생성 코드를 넣는 것이 도움이 될까요?
RickDavis 2012-06-23

@RaoG 아니요 . 동기화 블록에서 확인 생성을 모두 원합니다 . 이 두 작업이 중단없이 함께 수행되어야합니다. 그렇지 않으면 위에서 설명한 상황이 발생할 수 있습니다.
Oleksi 2012-06-23

7

싱글 톤 열거

스레드로부터 안전한 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 ;
  }
}

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

정적 코드 블록을 사용하여 클래스로드시 인스턴스를 인스턴스화하고 스레드 동기화 문제를 방지 할 수도 있습니다.

public class MySingleton {

  private static final MySingleton instance;

  static {
     instance = new MySingleton();
  }

  private MySingleton() {
  }

  public static MySingleton getInstance() {
    return instance;
  }

}

@Vimsha 다른 몇 가지. 1. 당신은 instance최종적으로 만들어야 getInstance()합니다. 2. 당신은 정적으로 만들어야 합니다.
John Vint

싱글 톤에서 스레드를 생성하려면 어떻게 하시겠습니까?
Arun George

@ arun-george는 스레드 풀, 필요한 경우 단일 스레드 풀을 사용하고, 어떤 오류에 관계없이 스레드가 절대 죽지 않도록하려면 while (true) -try-catch-throwable로 둘러 쌉니다.
tgkprog 2015

0

다중 스레드 환경에서 Java로 Singleton을 구현하는 가장 좋은 방법은 무엇입니까?

Singleton을 구현하는 가장 좋은 방법은이 게시물을 참조하십시오.

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

여러 스레드가 동시에 getInstance () 메서드에 액세스하려고하면 어떻게됩니까?

메서드를 구현 한 방식에 따라 다르며, 휘발성 변수없이 이중 잠금을 사용하면 부분적으로 구성된 Singleton 객체를 얻을 수 있습니다.

자세한 내용은이 질문을 참조하십시오.

이 이중 체크 잠금 예제에서 휘발성이 사용되는 이유

싱글 톤의 getInstance ()를 동기화 할 수 있습니까?

Singleton 클래스를 사용할 때 동기화가 정말로 필요합니까?

아래 방법으로 Singleton을 구현하는 경우 필요하지 않습니다.

  1. 정적 초기화
  2. 열거 형
  3. Initialization-on-demand_holder_idiom을 사용한 LazyInitalaization

자세한 내용은이 질문을 참조하십시오.

자바 싱글 톤 디자인 패턴 : 질문


0
public class Elvis { 
   public static final Elvis INSTANCE = new Elvis();
   private Elvis () {...}
 }

출처 : 효과적인 자바-> 항목 2

클래스가 항상 싱글 톤으로 유지된다는 확신이 들면 그것을 사용하는 것이 좋습니다.

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