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

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

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

public Object aquireMonitor(ResourceIdentifier resourceIdentifier) {
  concurrentMap.putIfAbsent(resourceIdentifier, resourceIdentifier);
  return concurrentMap.get(resourceIdentifier);
}

访问资源后,我将同步对aquireMonitor返回的监视对象的访问。据我了解ConcurrentHashMap的实现,这并不一定会阻塞所有线程(我阅读this blog article是为了了解实现。),我的应用程序可以愉快地运行,而不会存在并发访问以前丑陋引入的资源之一的危险。在极少数情况下的错误。

但是:我的应用程序管理大量资源,并且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;
}
CONCURRENCY_LEVEL当然是一个静态字段。

我的想法是这样的:每当一个监视器仍被另一个线程使用时,它当然都会拥有对该监视器的(强烈)引用。因此,ConcurrentMap中的条目将不会被垃圾收集,并且当两个线程想要访问同一资源时,可以保证监视器是共享的。 (循环解决了putIfAbsentget调用之间可能的垃圾回收。)

但是,MapMaker.weakKeys打破了equals找到条目的约定,而是使用标识。

现在我想知道:有人知道从这里去哪里吗?还是这种方法是个坏主意?还有一个附带的问题:如果仅使用weakValues,是否会将整个条目从地图中删除?还是地图的键总是有另一个强大的参考?感谢帮助!

PS:我的第一个猜测是我应该从地图迁移到缓存。这也许是最好的解决方案?我以前从未使用过Guava,但是现在我发现在缓存键比较方面存在相同的限制。

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

最佳答案

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

我建议您切换到缓存,或禁止使用SoftReferences切换到ConcurrentMap-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();
    }
}

等等。这是很多方法的总结,因此我建议您改用EHCache之类的方法。

07-26 03:03