具有弱键的并发映射

Raf*_*ter 6 java collections concurrency multithreading guava

我有一个高度并发的应用程序,它利用文件系统上的资源.两个线程可能同时访问同一资源的机会相当小,但如果发生这种情况,应用程序可能会显示有线行为.

每个资源都可以通过String坐标向量(捆绑在一个类中ResourceIdentifier)进行映射.在我当前的解决方案中,我创建了一个ConcurrentMap这样的资源标识符来收集线程在访问资源时使用的监视器:( ResourceIdentifier覆盖equalshashCode正确.)

ConcurrentMap<ResourceIdentifier, ResourceIdentifier> concurrentMap 
   = new ConcurrentHashMap<>();

public Object aquireMonitor(ResourceIdentifier resourceIdentifier) {
  concurrentMap.putIfAbsent(resourceIdentifier, resourceIdentifier);
  return concurrentMap.get(resourceIdentifier);
}
Run Code Online (Sandbox Code Playgroud)

访问资源时,我同步对返回的监视器对象的访问权限aquireMonitor.据我了解的实现ConcurrentHashMap,这并不一定会阻止所有线程(我读这篇博客文章是为了理解实现.)我的应用程序可以愉快地运行而不会有并发访问以前引入的资源之一的危险在极少数情况下出现丑陋的错误.

但是:我的应用程序管理大量资源,并concurrentMap随着运行时增长.这就是我现在尝试向我的应用程序添加弱引用语义的原因(通过使用Guava):

ConcurrentMap<ResourceIdentifier, ResourceIdentifier> concurrentMap 
   = new MapBuilder().weakValues().weakKeys()
     .concurrencyLevel(CONCURRENCY_LEVEL).makeMap();

public Object aquireMonitor(ResourceIdentifier resourceIdentifier) {
  ResourceIdentifier monitor;
  do {
    concurrentMap.putIfAbsent(resourceIdentifier, resourceIdentifier);
    monitor = concurrentMap.get(resourceIdentifier);
  } while(monitor == null);
  return monitor;
}
Run Code Online (Sandbox Code Playgroud)

CONCURRENCY_LEVEL 当然是一个静态领域.

我的想法是这样的:每当一个监视器仍然被另一个线程使用时,它当然会持有(强)对该监视器的引用.因此,ConcurrentMap不会对垃圾中的条目进行垃圾收集,并保证在两个线程想要访问同一资源时共享监视器.(该循环解决了putIfAbsent和之间调用之间可能的垃圾收集get.)

但是,MapMaker.weakKeys打破条目找到的合同equals并使用标识.

现在我想知道:有人知道从哪里开始吗?或者这种方法反正是一个坏主意?并且作为一个附带问题:如果我只使用了整个条目,是否会从地图中删除weakValues?或者地图总是会通过其键有另一个强引用?感谢帮助!

PS:我的第一个猜测是我应该从地图迁移到缓存.这可能是最好的解决方案吗?我以前从未使用过番石榴,但是现在我发现对缓存的密钥比较有相同的限制.

PPS:我无法在文件系统上创建锁.(不是我的电话.)

Zim*_*oot 6

您将需要键和值的弱引用。

我建议您切换到缓存,或者禁止切换到ConcurrentMap带有SoftReferences的缓存- GC 渴望收集弱引用,因此它们并不真正适合缓存,而它会延迟软引用的收集,而仍然不允许他们导致OutOfMemoryError. 要实现软引用并发映射,您将创建一个ConcurrentMap包装器,例如

class SoftConcurrentMap<K, V> extends ConcurrentHashMap<SoftReference<K>, SoftReference<V>> {
    ConcurrentHashMap<SoftReference<K>, SoftReference<V>> map = new ConcurrentHashMap<>();

    V public void get(Object key) {
        SoftReference<V> value = map.get(new SoftRefrence(key));
        if(value != null && value.get() != null) {
            return value.get();
        } else {
            map.remove(new SoftReference(key));
            return null;
        }
    }

    V put(K key, V value) {
        SoftReference<V> oldValue = map.put(new SoftReference(key), new SoftReference(value));
        return oldValue == null ? null : oldValue.get();
    }
}
Run Code Online (Sandbox Code Playgroud)

等等。这是很多方法的包装,因此我建议您改用EHCache 之类的东西。