2012-11-19 1 views
1

(아마도 "How to create a generic singleton class in java?" :제네릭 클래스에 대한 모든 유형 인수에 대해 별도의 싱글 톤을 만드는 데 가장 적합한 관용구?

class MyClass<T> { 
    private static Map<Class<MyClass<?>>, MyClass<?>> s_instances = 
     new HashMap<Class<MyClass<?>>, MyClass<?>>(); 

    public static MyClass<?> blah(Class<MyClass<?>> clz) 
      throws InstantiationException, IllegalAccessException { 
     if (s_instances.get(clz) != null) 
      return s_instances.get(clz); 
     MyClass<?> instance = clz.newInstance(); 
     s_instances.put(clz, instance); 
     return instance; 
    } 
} 

상보 질문은 싱글 형식 당 인수-값을 갖는위한 더 나은 관용구가 있습니까?

편집 : 스레드 안전성의 부족을 지적하기 위해 대답하지 마십시오. 요점을 알았어. 나는이지도보다 더 우아한 무언가를 할 수 있는지 묻는다.

+0

유형별로 별도의 인스턴스를 갖는 요지는 귀하의 경우에 분명하지 않습니다. 새 인스턴스는 MyClass의 정확한 생성자 (서브 클래스가 아님)이어야하므로 MyClass의 생성자를 호출 할 수 있습니다 (리플렉션 필요 없음). – artbristol

답변

1

제발 그렇게하지 마십시오.
A. 싱글 톤은 스레드로부터 안전하지 않습니다.
B. double check pattern issues at Java에 유의하십시오. -
가 1. 당신은 아마 정의 할 수 있습니다

static { 
    instance = new MySingleton(); 
} 

다음

public static MySingleton getInstance() { 
return instance 
} 

을 그리고 당신이 정말로 주장하는 경우 :
C.는 정말 열심히 각 클래스의 정적 초기화를 가지고하는 것입니다
2. 원하는 유형을 거기에 등록 할 수 있습니다 (개인 CTOR이있는 것이 좋습니다). 예를 들어, 맵에있는 유형을 관리하는 싱글 톤 (키는 클래스 또는 전체 클래스 이름이고 값은 객체입니다.
3. this 답을 사용하여 개인 CTOR을 호출하고 맵 항목의 값에 배치 할 인스턴스를 만듭니다.
4.의 서명, 1에서 언급 된 repoistory에 getInstance 메소드를 제공합니다 :

public Object getInstanceByType(Class<?> clazz) 

이 방법은 내부지도에서 인스턴스를 얻을 것이다.

+0

스레드 안전성에 대한 포인트입니다 (특정 용도에는 문제가되지 않지만). 왜 내가하지 않을 것을 권하겠습니까? – einpoklum

+0

이것은 실제 값이 거의없는 인프라를 코더에 추가하기 때문입니다. 클래스 당 6 줄의 코드로 클래스 당 싱글 톤을 가질 수 있습니다 - 인프라를 추가할만한 가치가 있습니까? –

+0

나는 따라하지 않는다 ... 나는 type 매개 변수의 값이 무엇인지 모르겠다. 따라서이 6 줄의 코드를 추가 할 수 없다. – einpoklum

1

귀하의 방법은 스레드로부터 안전하지 않습니다 : 하나 개의 스레드 라인이 바뀌지

private static Map<Class<MyClass<?>>, MyClass<?>> s_instances = 
    new HashMap<Class<MyClass<?>>, MyClass<?>>(); 

public static MyClass<?> blah(Class<MyClass<?>> clz) 
     throws InstantiationException, IllegalAccessException { 
    if (s_instances.get(clz) != null) 
     return s_instances.get(clz); 
    // here1 
    MyClass<?> instance = clz.newInstance(); 
    s_instances.put(clz, instance); 
    // here2 
    return instance; 
} 

되면이 //here1 표시, 첫 번째 스레드가 따라서 두 번째를 작성, //here2 표시 줄에 전에 두 번째 스레드가 방법을 입력 할 수 있습니다 같은 종류의 "싱글 톤"을 만들고지도의 첫 번째를 덮어 씁니다.

빠른-수정지도를 동기화하는 것입니다 :

public static MyClass<?> blah(Class<MyClass<?>> clz) 
     throws InstantiationException, IllegalAccessException { 
    synchronized(s_instances){ 
    if (s_instances.get(clz) != null) 
     return s_instances.get(clz); 
    // here1 
    MyClass<?> instance = clz.newInstance(); 
    s_instances.put(clz, instance); 
    // here2 
    return instance; 
    } 
} 

그러나, 많은 스레드가 결국 아마도 응용 프로그램을 죽이고, 많은 시간을 기다려야 할 것이라는 점을 의미 할 것입니다. 아마 당신이 할 일은 두 단계 솔루션입니다 :

private static Map<Class<MyClass<?>>, MyClass<?>> s_instances = 
    Collections.synchronizedMap(new HashMap<Class<MyClass<?>>, MyClass<?>>()); 

를하거나 함께 이동 : 또한

public static MyClass<?> blah(Class<MyClass<?>> clz) 
     throws InstantiationException, IllegalAccessException { 
    Object candidate = s_instances.get(clz); 
    if(clz.isInstance(candidate)){ // implicit null check 
     return clz.cast(candidate); 
    } 
    synchronized(s_instances){ 
    Object candidate = s_instances.get(clz); 
    if(clz.isInstance(candidate)){ // gotta check a second time in a 
     return clz.cast(candidate); // synchronized context 
    } 
    MyClass<?> instance = clz.newInstance(); 
    s_instances.put(clz, instance); 
    return instance; 
    } 
} 

당신이 Collections.synchronizedMap()에 포장해야 하나, 그래서는 HashMap은 동시 액세스에 적합하지 않습니다 ConcurrentHashMap 대신

+0

이것이 내가 내 대답에 유형의 사전 등록을 제안한 이유입니다. 그는 내부 맵에 N 개의 항목을 넣는 사전 정의 된 맵을 사용하여 잠금 시간을 줄이려고 항상 노력할 수 있습니다. ConcurrentMap과 완전히 똑같지는 않습니다. 왜냐하면 그는 "세그먼트"를 관리 할 것이고 애플리케이션이 시작될 때 그것들 모두를 생성 할 것이기 때문에 새로운 세그먼트를 추가 할 때 잠글 필요가 없습니다. –

관련 문제