【发布时间】:2013-06-28 10:46:53
【问题描述】:
我有一个高度并发的应用程序,它利用文件系统上的资源。两个线程同时访问同一资源的可能性很小,但如果发生这种情况,应用程序可能会显示有线行为。
每个资源都可以通过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:我无法在文件系统上创建锁。 (不是我的电话。)
【问题讨论】:
标签: java multithreading collections concurrency guava