迭代WeakHashMap

我正在同时使用WeakHashMap。 我想基于Integer参数实现细粒度锁定; 如果线程A需要修改由Integer a标识的资源,并且线程B对由Integer b标识的资源执行相同的操作,则它们不需要同步。 但是,如果有两个线程使用相同的资源,假设线程C也使用由整数a标识的资源,那么当然线程A和C需要在同一个锁上同步。

当没有更multithreading需要具有ID X的资源时,可以删除Map中用于key = X的Lock。 但是,另一个线程可以在此时进入并尝试在Map中使用ID = X的锁,因此在添加/删除锁时我们需要全局同步。 (这将是每个线程必须同步的唯一地方,无论Integer参数如何)但是,线程无法知道何时删除锁,因为它不知道它是使用锁的最后一个线程。

这就是我使用WeakHashMap的原因:当不再使用ID时,可以在GC需要时删除键值对。

为了确保我对已存在的条目的键具有强引用,并且确切地说是构成映射关键字的对象引用,我需要迭代映射的keySet:

 synchronized (mrLocks){ // ... do other stuff for (Integer entryKey : mrLocks.keySet()) { if (entryKey.equals(id)) { key = entryKey; break; } } // if key==null, no thread has a strong reference to the Integer // key, so no thread is doing work on resource with id, so we can // add a mapping (new Integer(id) => new ReentrantLock()) here as // we are in a synchronized block. We must keep a strong reference // to the newly created Integer, because otherwise the id-lock mapping // may already have been removed by the time we start using it, and // then other threads will not use the same Lock object for this // resource } 

现在,地图的内容可以在迭代时改变吗? 我想不是,因为通过调用mrLocks.keySet() ,我创建了对迭代范围的所有键的强引用。 那是对的吗?

由于API没有对keySet()做出任何断言,我建议使用这样的缓存:

 private static Map> lockCache = Collections.synchronizedMap(new WeakHashMap<>()); public static Object getLock(Integer i) { Integer monitor = null; synchronized(lockCache) { Reference old = lockCache.get(i); if (old != null) monitor = old.get(); // if no monitor exists yet if (monitor == null) { /* clone i for avoiding strong references to the map's key besides the Object returend by this method. */ monitor = new Integer(i); lockCache.remove(monitor); //just to be sure lockCache.put(monitor, new WeakReference<>(monitor)); } } return monitor; } 

这样你就可以在锁定它的同时保持对监视器(键本身)的引用,并允许GC在不再使用时完成它。

编辑:
在评论中有关有效载荷的讨论之后,我想到了一个带有两个缓存的解决方案:

 private static Map> lockCache = new WeakHashMap<>(); private static Map keyCache = new WeakHashMap<>(); public static ReentrantLock getLock(Integer i) { ReentrantLock lock = null; synchronized(lockCache) { Reference old = lockCache.get(i); if (old != null) lock = old.get(); // if no lock exists or got cleared from keyCache already but not from lockCache yet if (lock == null || !keyCache.containsKey(lock)) { /* clone i for avoiding strong references to the map's key besides the Object returend by this method. */ Integer cacheKey = new Integer(i); lock = new ReentrantLock(); lockCache.remove(cacheKey); // just to be sure lockCache.put(cacheKey, new WeakReference<>(lock)); keyCache.put(lock, cacheKey); } } return lock; } 

只要存在对有效负载(锁)的强引用, keyCache对映射整数的强引用就可以避免从lockCache缓存中删除有效负载。