이 Java Singleton이 WebSphere 6에서 반복적으로 재건 될 수 있습니까?

StackOverflow https://stackoverflow.com/questions/225895

  •  03-07-2019
  •  | 
  •  

문제

시스템에서 문제를 추적하려고 노력하고 있으며 다음 코드가 저를 걱정합니다. 다음은 1 차 서블릿의 dopost () 메소드에서 발생합니다 (유죄를 보호하기 위해 이름이 변경되었습니다).

...
if(Single.getInstance().firstTime()){
   doPreperations();
}
normalResponse();
...

싱글 톤 '싱글'은 다음과 같습니다.

private static Single theInstance = new Single();

private Single() {
...load properties...
}

public static Single getInstance() {
    return theInstance;
}

getInstance () 메소드에서 널 텐스 턴스를 확인하는 대신 정적 이니셜 라이저를 사용하도록 설정되어 있으면 계속해서 재건 될 수 있습니까?

추신 - 우리는 Java 1.4의 앱으로 WebSphere 6을 실행하고 있습니다.

도움이 되었습니까?

해결책

나는 이것을 Sun의 사이트에서 찾았습니다.

다른 클래스 로더에 의해 동시에로드 된 여러 싱글 톤

두 개의 클래스 로더가 클래스를로드하면 실제로 클래스의 사본이 두 개이며 각로드 사본은 고유 한 싱글 톤 인스턴스를 가질 수 있습니다. 이는 특히 특정 서블릿 엔진 (예 : IPLANET)에서 실행되는 서블릿과 관련이 있으며, 각 서블릿은 기본적으로 자체 클래스 로더를 사용합니다. 공동 싱글 톤에 액세스하는 두 개의 다른 서블릿은 실제로 두 개의 다른 개체를 얻습니다.

여러 클래스 로더가 생각보다 더 일반적으로 발생합니다. 브라우저가 애플릿으로 사용하기 위해 네트워크에서 클래스를로드하면 각 서버 주소에 대해 별도의 클래스 로더를 사용합니다. 마찬가지로 Jini 및 RMI 시스템은 클래스 파일을 다운로드하는 다른 코드베이스에 별도의 클래스 로더를 사용할 수 있습니다. 자체 시스템에서 사용자 정의 클래스 로더를 사용하는 경우 동일한 문제가 모두 발생할 수 있습니다.

다른 클래스 로더로로드 된 경우, 동일한 이름, 동일한 패키지 이름조차도 동일한 이름을 가진 두 개의 클래스는 뚜렷한 것으로 취급됩니다. 실제로는 동일한 클래스의 바이트로 바이트가 되더라도. 다른 클래스 로더는 클래스를 구별하는 다른 네임 스페이스를 나타냅니다 (클래스 이름이 동일하더라도) MySingleton 수업은 실제로 뚜렷합니다. (자원에서 "네임 스페이스 메커니즘으로 클래스 로더"를 참조하십시오.) 두 개의 싱글 톤 객체는 같은 이름의 두 클래스에 속하므로, 처음에는 동일한 클래스의 두 개의 싱글 톤 객체가 있다는 것을 알게됩니다.

소환.

위의 문제 외에도 if firstTime() 동기화되지 않으면 스레딩 문제도있을 수 있습니다.

다른 팁

아니요 반복해서 건설되지 않습니다. 정적이므로 클래스 로더가 처음으로 수업을 터치 할 때 바로 한 번만 구성됩니다.

예외 만 - 여러 클래스 로더가있는 경우.

(에서 괴짜):

alt text

다른 사람들이 언급했듯이, 정적 이니셜 라이저는 클래스 로더 당 한 번만 실행됩니다.

내가 볼만한 한 가지는 firstTime() 방법 - 왜 일을 할 수 없는가 doPreparations() 싱글 톤 자체 내에서 처리됩니까?

불쾌한 종속성 세트처럼 들립니다.

정적 이니셜 라이저 사용과 게으른 초기화 사이에는 전혀 차이가 없습니다. 실제로 게으른 초기화를 엉망으로 만드는 것이 훨씬 쉽고 동기화를 시행합니다. JVM은 클래스에 액세스하기 전에 항상 정적 이니셜 라이저가 실행되도록 보장하며 한 번만 한 번만 발생합니다.

즉, JVM은 귀하의 클래스가 한 번만로드 될 것이라고 보장하지 않습니다. 그러나 두 번 이상로드 되더라도 웹 응용 프로그램은 웹 애플리케이션 클래스 로더 또는 부모에로드되므로 관련 싱글 톤 만 여전히 표시됩니다. 여러 웹 애플리케이션이 배포 된 경우 각 응용 프로그램마다 첫 번째 ()가 한 번 호출됩니다.

확인해야 할 가장 분명한 점은 처음 ()을 동기화해야하며 해당 메소드를 종료하기 전에 처음으로 플래그가 설정되어 있다는 것입니다.

아니요, '단일'의 여러 사본을 생성하지 않습니다. (클래스 로더 문제는 나중에 방문됩니다)

당신이 설명한 구현은 Briant Goetz의 책에서 '열심 인 초기화'로 설명됩니다.실제로 Java 동시성'.

public class Single
{
    private static Single theInstance = new Single();

    private Single() 
    { 
        // load properties
    }

    public static Single getInstance() 
    {
        return theInstance;
    }
}

그러나 코드는 원하는 것이 아닙니다. 인스턴스가 생성 된 후 코드가 게으른 초기화를 수행하려고합니다. 이를 위해서는 모든 클라이언트 라이브러리가 사용하기 전에 '처음 ()/dopreparation ()'를 수행해야합니다. 코드를 매우 깨지기 쉬운 올바른 일을하기 위해 고객에게 의존 할 것입니다.

중복 코드가 없도록 코드를 다음과 같이 수정할 수 있습니다.

public class Single
{
    private static Single theInstance = new Single();

    private Single() 
    { 
        // load properties
    }

    public static Single getInstance() 
    {   
        // check for initialization of theInstance
        if ( theInstance.firstTime() )
           theInstance.doPreparation();

        return theInstance;
    }
}

불행히도, 이것은 게으른 초기화의 열악한 구현이며 동시 환경 (J2EE 컨테이너)에서는 작동하지 않습니다.

싱글 톤 초기화, 특히 메모리 모델에 관한 많은 기사가 있습니다. JSR 133 Java 1.5 및 1.6의 Java 메모리 모델에서 많은 약점을 해결했습니다.

Java 1.5 & 1.6을 사용하면 몇 가지 선택이 있으며이 책에서 언급되어 있습니다.효과적인 자바'Joshua Bloch에 의해.

  1. 위의 초기 조치, 위의 [EJ 항목 3
  2. 게으른 비화 소지자 클래스 관용구 [EJ 항목 71
  3. 열거 유형 [EJ 항목 3
  4. '휘발성'정적 필드로 이중 점검 잠금 [EJ 항목 71

솔루션 3과 4는 Java 1.5 이상에서만 작동합니다. 따라서 최상의 솔루션은 #2입니다.

다음은 psuedo-implementation입니다.

public class Single
{
    private static class SingleHolder
    {
        public static Single theInstance = new Single();
    }

    private Single() 
    { 
        // load properties
        doPreparation();
    }

    public static Single getInstance() 
    {
        return SingleHolder.theInstance;
    }
}

'dopreparation ()'는 생성자 내부에 있으므로 올바르게 초기화 된 인스턴스를 얻을 수 있습니다. 또한 JVM의 게으른 클래스 로딩을 다시 피고하고 동기화 'getInstance ()'가 필요하지 않습니다.

당신이 정적 필드 인 theinstance라는 것을 알아 차린 한 가지 '최종'이 아닙니다. Java Concurrency의 예에는 '최종'이 없지만 EJ는 그렇습니다. 아마도 James 's는'Classloader '에 대한 그의 답변과'Final '의 요구 사항에 더 많은 색상을 추가 할 수 있습니다.

그렇게 말하면서 '정적 최종'을 사용하면 부작용이 있습니다. Java 컴파일러는 '정적 최종'을보고 가능한 한 많이 인라인을 시도 할 때 매우 공격적입니다. 이것은 언급되어 있습니다 Jeremy Manson의 블로그 게시.

간단한 예입니다.

파일 : a.java

public class A
{
    final static String word = "Hello World";
}

파일 : B.java

public class B
{
    public static void main(String[] args) {
        System.out.println(A.word);
    }
}

A.Java와 B.java를 모두 컴파일하면 A.Java를 다음으로 변경합니다.

파일 : a.java

public class A
{
    final static String word = "Goodbye World";
}

당신은 'a.java'와 b.class를 다시 컴파일합니다. 당신이 얻을 출력은입니다

Hello World

클래스 로더 문제의 경우, 대답은 예입니다. 여러 클래스 로더에 하나 이상의 싱글 톤 인스턴스를 가질 수 있습니다. 자세한 내용은 찾을 수 있습니다 위키 백과. 특정 기사도 있습니다 WebSphere.

싱글 톤 구현 (싱글 톤을 전혀 사용하지 않는 것 외에는)에 대해 내가 바꿀 유일한 것은 인스턴스 필드를 최종적으로 만드는 것입니다. 정적 필드는 클래스로드에서 한 번 초기화됩니다. 수업은 게으르게로드되므로 효과적으로 게으른 인스턴스화를 무료로 얻습니다.

물론 별도의 클래스 로더에서로드되면 여러 "싱글 톤"이 발생하지만 Java의 모든 싱글 톤 관용의 한계입니다.

편집하다: 처음 () 및 dopreparations () 비트는 의심스러워 보입니다. 싱글 톤 인스턴스의 생성자로 이동할 수 없습니까?

아니오 - 정적 초기화 instance 한 번만 끝날 것입니다. 고려해야 할 두 가지 :

  • 이것은 스레드-안전이 아닙니다 (인스턴스는 메인 메모리에 "게시되지 않았다"
  • 당신의 firstTime 적절하게 동기화되지 않는 한 메소드는 아마도 여러 번 호출 될 수 있습니다.

이론적으로는 한 번만 만들어 질 것입니다. 그러나이 패턴은 다양한 애플리케이션 서버에서 중단되며, 여기서 'Singleton'클래스의 여러 인스턴스를 얻을 수 있습니다 (스레드 안전이 아니기 때문에).

또한 싱글 톤 패턴은 많이 비판되었습니다. 예를 들어 참조하십시오 싱글 톤 나는 당신을 사랑하지만, 당신은 나를 실망시키고 있습니다

클래스가 클래스 로더로로드 될 때만 한 번만로드됩니다. 이 예제는 더 나은 싱글 톤 구현을 제공하지만 가능한 한 게으른로드 및 스레드 안전입니다. 또한 모든 알려진 버전의 Java에서 작동합니다. 이 솔루션은 다양한 Java 컴파일러 및 가상 머신에서 가장 휴대용입니다.


public class Single {

private static class SingleHolder {
   private static final Single INSTANCE = new Single();
}

private Single() {
...load properties...
}

public static Single getInstance() {
    return SingleHolder.INSTANCE;
}

}

내부 클래스는 GetInstance ()가 호출되는 순간보다 더 이전에 참조되지 않으므로 클래스 로더에 의해로드되지 않습니다. 따라서,이 솔루션은 특수 언어 구성 (즉 휘발성 및/또는 동기화 된)이 필요하지 않고 스레드 안전이다.

단일 인스턴스가 최종적이어야하는 것은 필수적이지 않습니다 (다른 패턴을 사용하여 동작을 전환하는 것을 피할 수 있기 때문에 실제로는 전혀 좋은 생각이 아닙니다).

아래 코드에서 한 번만 인스턴스화되는 방법을 확인할 수 있습니다 (생성자를 처음 호출 할 때)

패키지 날짜;

import java.util.date;

공개 클래스 usdatefactory 구현 날짜 factory {private static usdatefactory usdatefactory = null;

private USDateFactory () { }

public static USDateFactory getUsdatefactory() {
    if(usdatefactory==null) {
        usdatefactory = new USDateFactory();
    }
    return usdatefactory;
}

public String getTextDate (Date date) {
    return null;
}

public NumericalDate getNumericalDate (Date date) {
    return null;
}

}

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top