Java 정적 이니셜 라이저 스레드는 안전합니까?


136

정적 코드 블록을 사용하여 레지스트리의 일부 컨트롤러를 초기화하고 있습니다. 내 질문은 따라서이 정적 코드 블록이 클래스가 처음로드 될 때 절대적으로 한 번만 호출된다는 것을 보장 할 수 있습니까? 이 코드 블록이 언제 호출되는지 보장 할 수 없다는 것을 이해합니다. 클래스 로더가 처음로드 할 때 추측합니다. 정적 코드 블록의 클래스에서 동기화 할 수 있다는 것을 알고 있지만 실제로는 이것이 실제로 어떻게됩니까?

간단한 코드 예제는 다음과 같습니다.

class FooRegistry {

    static {
        //this code must only ever be called once 
        addController(new FooControllerImpl());
    }

    private static void addController(IFooController controller) { 
        // ...
    }
}

아니면 내가해야합니까;

class FooRegistry {

    static {
        synchronized(FooRegistry.class) {
            addController(new FooControllerImpl());
        }
    }

    private static void addController(IFooController controller) {
        // ...
    }
}

10
테스트 할 수 없기 때문에이 디자인이 마음에 들지 않습니다. Dependency Injection을 살펴보십시오.
dfa

답변:


199

예, Java 정적 초기화 프로그램은 스레드로부터 안전합니다 (첫 번째 옵션 사용).

그러나 코드가 정확히 한 번만 실행되도록하려면 단일 클래스 로더 만 클래스를로드해야합니다. 정적 초기화는 클래스 로더 당 한 번 수행됩니다.


2
그러나 클래스는 여러 클래스 로더에 의해로드 될 수 있으므로 호출을 동기화하는지 여부에 관계없이 addController가 여전히 두 번 이상 호출 될 수 있습니다.
Matthew Murdoch

4
아 잠깐만, 그래서 정적 코드 블록은 실제로 클래스를로드하는 모든 클래스 로더에 대해 호출된다고 말하고 있습니까? 흠 ... 그래도 괜찮을 것
같지만, 여러

1
예. 정적 코드 블록은 클래스를로드하는 모든 클래스 로더에 대해 호출됩니다.
Matthew Murdoch

3
@ simon622 예. 그러나 각 ClassLoader의 다른 클래스 객체에서 작동합니다. 정규화 된 이름은 동일하지만 서로 캐스트 할 수없는 다른 유형을 나타내는 다른 클래스 객체.
Erwin Bolwidt

1
이는 'final'키워드가 en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom 의 인스턴스 홀더에서 중복됨을 의미 합니까?
spc16670

11

게으른 초기화에 사용할 수있는 트릭입니다

enum Singleton {
    INSTANCE;
}

또는 Java 5.0 이전

class Singleton {
   static class SingletonHolder {
      static final Singleton INSTANCE = new Singleton();
   }
   public static Singleton instance() {
      return SingletonHolder.INSTANCE;
   }
}

SingletonHolder의 정적 블록은 스레드 안전 방식으로 한 번 실행되므로 다른 잠금이 필요하지 않습니다. singletonHolder 클래스는 instance ()를 호출 할 때만로드됩니다.


18
정적 블록은 전역 적으로 한 번만 실행된다는 사실을 바탕 으로이 답변을 기반으로합니다. 이것은 바로 질문이었습니다.
Michael Myers

2
멀티 클래스 로더 환경에서는 이것이 안전하지 않다고 생각합니다.
Ahmad

2
@Ahmad 멀티 클래스 로더 환경은 각 애플리케이션이 자체 싱글 톤을 갖도록 설계되었습니다.
피터 로리

4

일반적인 상황에서는 정적 이니셜 라이저의 모든 것이 해당 클래스를 사용하는 모든 것보다 먼저 발생하므로 일반적으로 동기화가 필요하지 않습니다. 그러나 클래스는 정적 intiailiser가 호출하는 모든 항목에 액세스 할 수 있습니다 (다른 정적 초기자를 호출하는 것을 포함하여).

클래스는로드 된 클래스에 의해로드 될 수 있지만 반드시 즉시 초기화 될 필요는 없습니다. 물론 클래스는 클래스 로더의 여러 인스턴스에 의해로드 될 수 있으며 같은 이름을 가진 여러 클래스가 될 수 있습니다.


3

예, 일종의

static초기화는 그래서 정의 그것의 스레드 안전에 의해 한 번 호출됩니다 - 당신은 두 개 이상의 호출이 필요할 것 static조차 얻을 스레드 경합 이니셜 라이저.

즉, static이니셜 라이저는 다른 많은 방법으로 혼란스러워합니다. 그들이 부르는 순서는 실제로 없습니다. static이니셜 라이저가 서로 의존하는 두 개의 클래스가 있으면 혼란 스럽습니다 . 클래스를 사용하지만 static초기화 프로그램이 설정 한 것을 사용하지 않으면 클래스 로더가 정적 초기화 프로그램을 호출한다고 보장 할 수 없습니다.

마지막으로 동기화하려는 객체를 명심하십시오. 나는 이것이 실제로 당신이 요구하는 것이 아니라는 것을 알고 있지만, addController()스레드 안전 을 만들어야하는지 질문하지 않아야 합니다.


5
소스 코드의 순서에 따라 다음과 같이 정의 된 순서가 있습니다.
mafu

또한 결과를 사용하더라도 상관없이 항상 호출됩니다. 이 자바 6. 변경하지 않는 한
mafu

8
클래스 내에서 이니셜 라이저는 코드를 따릅니다. 두 개 이상의 클래스가 주어지면 어떤 클래스가 먼저 초기화되는지, 다른 클래스가 시작되기 전에 100 % 초기화되는지 또는 사물이 "인터리브"되는지에 따라 정의되지 않습니다. 예를 들어, 두 클래스가 각각 서로를 참조하는 정적 이니셜 라이저를 가지고 있다면 상황이 매우 빠릅니다. 이니셜 라이저를 호출하지 않고 다른 클래스에 정적 final int를 참조 할 수있는 방법이 있다고 생각했지만 다른 방법으로 논쟁하지는 않을 것입니다.
Matt

그것은 못 생겨서 피할 것입니다. 그러나주기를 해결하는 방법에는 정의 된 방법이 있습니다. "자바 프로그래밍 언어 4 판"인용 : 페이지 : 75, 섹션 : 2.5.3. 정적 초기화 : "사이클이 발생하면 X의 정적 초기화 프로그램은 Y의 메소드가 호출 된 지점까지만 실행됩니다. Y가 X 메소드를 호출하면 해당 메소드는 아직 실행되지 않은 나머지 정적 초기화 프로그램으로 실행됩니다. "
JMI MADISON

0

예, 정적 이니셜 라이저는 한 번만 실행됩니다. 자세한 내용은 이것을 읽으십시오 .


2
아니요, 두 번 이상 실행할 수 있습니다.
제한적 속죄

5
CLASSLOADER 당 한 번 실행할 수 없습니다.
ruurd

기본 답변 : 정적 초기화는 한 번만 실행됩니다. 고급 답변 : 정적 초기화는 클래스 로더 당 한 번 실행됩니다. 문구는이 두 가지 답변을 혼합하기 때문에 첫 번째 의견은 혼란 스럽다.
JMI MADISON

-4

따라서 기본적으로 싱글 톤 인스턴스를 원하기 때문에 어느 정도 구식 방식으로 싱글 톤 객체를 한 번만 초기화해야합니다.

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