6

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

每个资源都可以通过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,这并不一定会阻塞所有线程(我阅读了这篇博客文章以了解实现。)并且我的应用程序可以愉快地运行,而不会并发访问以前引入的资源之一的危险在非常罕见的情况下出现丑陋的错误。

但是:我的应用程序管理大量资源并且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:我无法在文件系统上创建锁。(不是我的电话。)

4

2 回答 2

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();
    }
}

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

于 2013-07-01T14:22:37.777 回答
0

我找到了另一个我实际实施的巧妙解决方案。也许一开始就想起来太容易了:

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

public Object aquireMonitor(ResourceIdentifier resourceIdentifier) {
  ResourceIdentifier monitor;
  do {
    concurrentMap.putIfAbsent(resourceIdentifier, new Object());
    monitor = concurrentMap.get(resourceIdentifier);
  } while(monitor == null);
  return monitor;
}

不使用时weakKeys。这就像一个魅力。键不再代表对用作监视器的实际对象的强引用,并且只要不再有线程持有对它们的强引用,映射的条目就会被垃圾收集。

于 2013-07-01T17:57:34.653 回答