我同时使用WeakHashMap。我想基于Integer参数实现细粒度锁定;如果线程A需要修改由Integer a
标识的资源,并且线程B对由Integer b
标识的资源执行相同操作,则它们不需要同步。但是,如果有两个线程使用相同的资源,假设线程C也使用由Integer a
标识的资源,那么当然线程A和C需要在同一个Lock上同步。
当没有更多线程需要具有ID X的资源时,可以删除Map for key for 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()
,我创建了对迭代范围的所有键的强引用。这是对的吗?
答案 0 :(得分:3)
由于API没有对keySet()做出任何断言,我建议使用这样的缓存:
private static Map<Integer, Reference<Integer>> lockCache = Collections.synchronizedMap(new WeakHashMap<>());
public static Object getLock(Integer i)
{
Integer monitor = null;
synchronized(lockCache) {
Reference<Integer> 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<Integer, Reference<ReentrantLock>> lockCache = new WeakHashMap<>();
private static Map<ReentrantLock, Integer> keyCache = new WeakHashMap<>();
public static ReentrantLock getLock(Integer i)
{
ReentrantLock lock = null;
synchronized(lockCache) {
Reference<ReentrantLock> 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
高速缓存中删除有效负载。