2015-01-19 4 views
10

WeakHashMap을 동시에 사용하고 있습니다. 나는 Integer 매개 변수를 기반으로 fine-grained locking을 원한다. 스레드 A가 정수 a으로 식별되는 자원을 수정해야하고 스레드 B가 정수 b으로 식별되는 자원에 대해 동일하게 수행하는 경우 이들을 동기화 할 필요가 없습니다. 그러나 같은 리소스를 사용하는 스레드가 두 개있는 경우 스레드 C는 Integer a으로 식별되는 리소스를 사용하고 스레드 A와 C는 동일한 Lock에서 동기화해야합니다.WeakHashMap 반복하기

ID X를 가진 자원이 필요한 스레드가 더 이상 없으면 key = X에 대한 맵의 잠금을 제거 할 수 있습니다. 그러나 다른 스레드가 그 순간에 들어 와서 Map for ID = X에서 잠금을 사용하려고 시도 할 수 있으므로 잠금을 추가/제거 할 때 전역 동기화가 필요합니다. (이것은 모든 스레드가 Integer 매개 변수와 상관없이 동기화해야하는 유일한 장소 일 것입니다.) 그러나 스레드는 잠금을 사용하는 마지막 스레드인지 모르기 때문에 잠금을 제거 할시기를 알 수 없습니다.

그래서 WeakHashMap을 사용하고 있습니다. ID가 더 이상 사용되지 않으면 GC가 필요할 때 키 - 값 쌍을 제거 할 수 있습니다.

synchronized (mrLocks){ 
    // ... do other stuff 
    for (Integer entryKey : mrLocks.keySet()) { 
     if (entryKey.equals(id)) { 
      key = entryKey; 
      break; 
     } 
    } 
    // if key==null, no thread has a strong reference to the Integer 
    // key, so no thread is doing work on resource with id, so we can 
    // add a mapping (new Integer(id) => new ReentrantLock()) here as 
    // we are in a synchronized block. We must keep a strong reference 
    // to the newly created Integer, because otherwise the id-lock mapping 
    // may already have been removed by the time we start using it, and 
    // then other threads will not use the same Lock object for this 
    // resource 
} 
:

내가 매핑의 키를 형성하는 기존 항목의 키에 대한 강한 참조, 정확히 그 객체 참조가 있는지 확인하려면, 나는지도의 키 집합을 반복 할 필요가

이제지도의 내용이 반복되는 동안 바뀔 수 있습니까? 나는 생각하지 않는다. 왜냐하면 mrLocks.keySet()을 호출함으로써 반복의 범위에 대한 모든 키에 대한 강력한 참조를 만들었 기 때문이다. 그 맞습니까? 에 잠금 동안

private static Map<Integer, Reference<Integer>> lockCache = Collections.synchronizedMap(new WeakHashMap<>()); 

public static Object getLock(Integer i) 
{ 
    Integer monitor = null; 
    synchronized(lockCache) { 
     Reference<Integer> old = lockCache.get(i); 
     if (old != null) 
      monitor = old.get(); 

     // if no monitor exists yet 
     if (monitor == null) { 
      /* clone i for avoiding strong references 
       to the map's key besides the Object returend 
       by this method. 
      */ 
      monitor = new Integer(i); 
      lockCache.remove(monitor); //just to be sure 
      lockCache.put(monitor, new WeakReference<>(monitor)); 
     } 

    } 

    return monitor; 
} 

당신이 모니터 (키 자체)에 대한 참조를 들고이 방법 : API가 키 집합()에 대해 아무런 주장을하지 않습니다으로

+0

참조 : http://stackoverflow.com/questions/2861410/weakhashmap-iteration-and-garbage-collection – ikettu

+1

[JavaDoc] (http://docs.oracle.com/javase/7/)에서 생각하지 않습니다. docs/api/java/util/WeakHashMap.html # keySet % 28 % 29) : * "** 세트는 맵에 의해 뒷받침되므로지도의 변경 사항은 세트에 반영되며, 그 반대의 경우도 마찬가지입니다. **" * – m0skit0

+0

@ m0skit0 아, 네가 맞을지도 모른다. 돌려 주어지는 Set에는 WeakReference도 포함됩니다 만, WeakHashMap가 그것을 숨기는 것처럼 이것은 숨겨집니다. 그래서 먼저 keySet의 복제본을 가져 와서 복제본을 반복해야만 강력한 참조가 포함 된 컬렉션을 반복하고 있는지 확인할 수 있습니다. – Timmos

답변

3

,이 같은 캐시 사용을 권장합니다 더 이상 사용하지 않으면 GC가이를 마무리 할 수 ​​있습니다.

편집 :만큼 페이로드 (잠금)에 대한 강한 참조로

private static Map<Integer, Reference<ReentrantLock>> lockCache = new WeakHashMap<>(); 
private static Map<ReentrantLock, Integer> keyCache = new WeakHashMap<>(); 

public static ReentrantLock getLock(Integer i) 
{ 
    ReentrantLock lock = null; 
    synchronized(lockCache) { 
     Reference<ReentrantLock> old = lockCache.get(i); 
     if (old != null) 
      lock = old.get(); 

     // if no lock exists or got cleared from keyCache already but not from lockCache yet 
     if (lock == null || !keyCache.containsKey(lock)) { 
      /* clone i for avoiding strong references 
       to the map's key besides the Object returend 
       by this method. 
      */ 
      Integer cacheKey = new Integer(i); 
      lock = new ReentrantLock(); 
      lockCache.remove(cacheKey); // just to be sure 
      lockCache.put(cacheKey, new WeakReference<>(lock)); 
      keyCache.put(lock, cacheKey); 
     }     
    } 

    return lock; 
} 

존재, 강한 참조 : 나는 두 캐시와 해결책에 대해 생각 코멘트에 페이로드에 대한 토론 후
keyCache의 매핑 된 정수에 매핑하면 lockCache 캐시에서 페이로드가 제거되지 않습니다.

+0

왜'Collections.synchronizedMap'을 사용하고 있습니까? 이미'synchronized' 키워드를 사용하는 외부 동기화가 있으므로 내부 동기화가 필요하지 않습니까? – Timmos

+0

@Timmos copy & pase :-) 당신이 옳습니다, 그것은 기본적으로 쓸데없고 제거 될 수 있습니다. – flo

+1

코드를 검사 한 결과이 코드는 충분히 깨끗해 보였습니다. 이미 이런 해결책을 생각했습니다. 이제 페이로드 (실제 Lock 객체)가 값에 포함되어야하므로 약한 참조 Integer 및 강력한 참조 된 Lock 객체를 캡슐화하는 새로운 클래스가 필요합니다. 기피. 올바른 Integer 인스턴스를 찾기 위해 키를 반복하는 대신 값으로 저장하면됩니다. 당신의 솔루션은 또한 유효한 접근법으로 보인다. – Timmos