我有一个高度并发的应用程序,它利用文件系统上的资源。两个线程将同时访问同一资源的可能性很小,但是如果发生这种情况,应用程序可能会显示有线行为。
每个资源都可以通过String
坐标的向量(捆绑在ResourceIdentifier
类中)映射。在当前解决方案中,我创建了此类资源标识符的ConcurrentMap
来收集线程在访问资源时使用的监视器:( ResourceIdentifier
正确覆盖了equals
和hashCode
。)
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
中的条目将不会被垃圾收集,并且当两个线程想要访问同一资源时,可以保证监视器是共享的。 (循环解决了putIfAbsent
和get
调用之间可能的垃圾回收。)但是,
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之类的方法。