我有一个高度并发的应用程序,它利用文件系统上的资源。两个线程同时访问同一资源的可能性很小,但如果发生这种情况,应用程序可能会显示有线行为。
每个资源都可以通过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
,这并不一定会阻塞所有线程(我阅读了这篇博客文章以了解实现。)并且我的应用程序可以愉快地运行,而不会并发访问以前引入的资源之一的危险在非常罕见的情况下出现丑陋的错误。
但是:我的应用程序管理大量资源并且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:我无法在文件系统上创建锁。(不是我的电话。)