2014-07-06 2 views
2

각 스레드에 대해 정확히 하나의 클래스 인스턴스를 생성하는 인스턴스 팩토리를 작성하는 좋은 방법을 찾고 있습니다 (즉, 스레드 특정 싱글 톤).스레드 특정 싱글 톤 인스턴스

가 이 첫째, 일반적으로 공장 정의하는 인터페이스

: 일반 싱글 들어

public interface Factory<T> { 
    public T create(); 
} 

, 나는 다음이 인터페이스의 구현을 만들 수는 랩

나 명확성을 위해 몇 가지 예제 코드를 추가하자 또 다른 공장 :

public class SingletonFactory<T> implements Factory<T> { 

    private final Factory<T> factory; 
    private T instance = null; 

    public SingletonFactory(Factory<T> factory) { 
     this.factory = factory; 
    } 

    @Override 
    public T create() { 
     if (instance==null) instance = factory.create(); 
     return instance; 
    } 
} 

기본적으로, create()를 호출 할 때 처음으로,이 호출이 CRE에 제공된 공장에 전달된다 객체의 한 인스턴스를 먹었습니다. 이 인스턴스는 캐시되고 create()에 대한 이후의 모든 호출은 동일한 인스턴스를 반환합니다. 내가해야 할 일은 객체 유형 T 주위에 SingletonFactory 인스턴스가 하나만 있는지 확인하는 것입니다.

public class ThreadSingletonFactory<T> implements Factory<T> { 

    private final Factory<T>  factory; 
    private final Map<Thread, T> instance; 

    public ThreadSingletonFactory(Factory<T> factory) { 
     this.factory = factory; 
     this.instance = new HashMap<Thread, T>(); 
    } 

    @Override 
    public T create() { 
     Thread thread = Thread.currentThread(); 
     T result = instance.get(thread); 
     if (result==null) { 
      result = factory.create(); 
      instance.put(thread, result); 
     } 
     return result; 
    } 
} 

이제 때마다 create()가 호출, 클래스가 현재 스레드를 조회 : 나는 이런 식으로 뭔가를 할 수 :

의 내가 스레드 당 객체 정확히 하나 개의 인스턴스를 제공하고자한다고 가정 해 봅시다 이 스레드가 이미 인스턴스가 생성되었는지 확인하기 위해 인스턴스 맵에 추가합니다. 그렇지 않으면 신선한 것을 만들고 그것을 기억합니다.

  1. 의 HashMap, 두 개의 스레드가 지금 같은 키가 작동하지 않습니다 때문에 문제가되지 않을 수도 있습니다 스레드로부터 안전하지 않습니다,하지만 난 아니에요 :

    나는이 순진한 접근 방식에 문제가 몇 참조 확실한. 난 그냥 Collections.synchronizedMap 안전하게 스레드를 만들 수 있지만 중요한 영향을 미칠 수있는 모든 곳에서 동기화 톤을 의미하기 때문에 나는 그것을 피하고 싶습니다.

  2. 응용 프로그램이 스레드 풀을 사용하지 않고 수명이 짧은 새 스레드를 계속 생성하는 경우지도가 잠재적으로 막대한 크기로 커지고 오래 걸리지 않는 인스턴스로 많은 메모리가 차단됩니다. 그것의 항목이 키가 더 이상 사용되지 일단 수집되지 쓰레기 할 수 있기 때문에

나는, 두 번째 문제를 해결하는 대신 WeakHashMap을 사용하여에보고 있었다,하지만 난 그뿐만 아니라 두 개의 잠재적 인 문제를 건너 왔어요 : OpenJDK Source 보면

  1. , expungeStaleEntries()를 통해 사용되지 않는 키의 해방 내가 get(...) 또는 put(...)를 호출하고, 다시, 나는 성능상의 이유로 회피하고자하는 잠재적으로 여러 동기화 작업을 포함 할 때마다 시작됩니다.
  2. 아직 동시성 문제가 발생하지 않도록하기 위해 synchronizedMap에 맵을 래핑 할 필요가 없다는 것이 확실하지 않습니다.

바람직하게 사용할 필요가없는 다른 용액 거기 임의synchronization (가정 Factory.create() 모든 구현 스레드 안전 있음)? 어쨌든 동시성을 관리해야하는 경우 java.util.concurrent.atomic의 클래스를 통해 그렇게하는 것이 좋습니다.

+7

http://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html –

+2

당신이 스레드 특정 개체를 할 때마다, ThreadLocal''로 본다. –

+0

@JBNizet, SotiriosDelimanolis, 귀하의 의견을 답변으로 바꾸려면 크레딧을 지불해야하는 크레딧을받을 수 있습니다. 당신이 옳은 해결책을 찾은 것 같아서요. –

답변

4

@JBNizet 및 @SotiriosDelimanolis에서 제안한대로 ThreadLocal이 트릭을 수행 할 수 있습니다.

아직 테스트하지는 않았지만이 경우 일 수 있습니까?

public class ThreadSingletonFactory<T> implements Factory<T> { 

    private final ThreadLocal<T> instance; 

    public ThreadSingletonFactory(final Factory<T> factory) { 
     this.instance = new ThreadLocal<T>() { 
      @Override 
      protected T initialValue() { 
       return factory.create(); 
      } 
     }; 
    } 

    @Override 
    public T create() { 
     return instance.get(); 
    } 
} 
+0

Java 8부터, 생성자 부분을'this.instance = ThreadLocal.withInInitial (factory :: create)'로 줄일 수 있습니다. – isnot2bad

0
I have used the implementation as explained above, I am able to achieve the single object per thread. 
Below is my complete implementation 
1. Created one singleton class MySingleTon with threadlocal object 
2. Created 2 thread class to use the MySingleTon object in the run method 
3. Created one class to create the thread object and also the MySingleTon object 
4. From the sysout statement, all MySingleTon reference are pointing to the same object 

    package example.test; 

    public class ThreadLevelSingleton 
    { 
     public static void main(String[] args) 
     { 
      Thread33 t3 = new Thread33(); 
      t3.start(); 

      Thread44 t4 = new Thread44(); 
      t4.start(); 

      MySingleTon m1 = MySingleTon.getInstance(); 
      MySingleTon m2 = MySingleTon.getInstance(); 

      System.out.println(Thread.currentThread().getName() + " : " + (m1 == m2)); 

      MySingleTon tm1 = t3.getMySingleTon(); 
      MySingleTon tm2 = t4.getMySingleTon(); 
      System.out.println(Thread.currentThread().getName() + " : " + (tm1.equals(tm2))); 
     } 
    } 

    class MySingleTon 
    { 
     private MySingleTon() 
     { 
     } 

     private static final ThreadLocal<MySingleTon> t = new ThreadLocal<MySingleTon>() 
     { 
      @Override 
      protected MySingleTon initialValue() 
      { 
       return new MySingleTon(); 
      } 
     }; 

     public static MySingleTon getInstance() 
     { 
      return t.get(); 
     } 
    } 

    class Thread33 extends Thread 
    { 
     MySingleTon m1; 

     @Override 
     public void run() 
     { 
      MySingleTon t = MySingleTon.getInstance(); 
      MySingleTon t1 = MySingleTon.getInstance(); 
      m1 = MySingleTon.getInstance(); 
      System.out.println(getName() + " : " + (t == t1)); 
      System.out.println(t); 
      System.out.println(t1); 
     } 

     MySingleTon getMySingleTon() 
     { 
      return m1; 
     } 
    } 

    class Thread44 extends Thread 
    { 
     MySingleTon m1; 

     @Override 
     public void run() 
     { 
      MySingleTon t = MySingleTon.getInstance(); 
      MySingleTon t1 = MySingleTon.getInstance(); 
      m1 = MySingleTon.getInstance(); 
      System.out.println(getName() + " : " + (t == t1)); 
      System.out.println(t); 
      System.out.println(t1); 
     } 

     MySingleTon getMySingleTon() 
     { 
      return m1; 
     } 
    }