迭代WeakHashMap [英] Iterating a WeakHashMap

查看:143
本文介绍了迭代WeakHashMap的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using a WeakHashMap concurrently. I want to achieve fine-grained locking based on an Integer parameter; if thread A needs to modify a resource identified by Integer a and thread B does the same for resource identified by Integer b, then they need not to be synchronized. However, if there are two threads using the same resource, say thread C is also using a resource identified by Integer a, then of course thread A and C need to synchronize on the same Lock.

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

When there are no more threads that need the resource with ID X then the Lock in the Map for key=X can be removed. However, another thread can come in at that moment and try to use the lock in the Map for ID=X, so we need global synchronization when adding/removing the lock. (This would be the only place where every thread must synchronize, regardless of the Integer parameter) But, a thread cannot know when to remove the lock, because it doesn't know it is the last thread using the lock.

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

That's why I'm using a WeakHashMap: when the ID is no longer used, the key-value pair can be removed when the GC wants it.

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

To make sure I have a strong reference to the key of an already existing entry, and exactly that object reference that forms the key of the mapping, I need to iterate the keySet of the map:

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(),我创建了对迭代范围的所有键的强引用。这是正确的吗?

Now, can the content of the Map change while iterating it? I think not, because by calling mrLocks.keySet(), I created a strong reference to all keys for the scope of iteration. Is that correct?

推荐答案

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

As the API makes no assertions about the keySet(), I would recommend a cache usage like this:

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在不再使用它时完成它。

This way you are holding a reference to the monitor (the key itself) while locking on it and allow the GC to finalize it when not using it anymore.

编辑:

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


After the discussion about payload in the comments I thought about a solution with two caches:

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 缓存中删除有效负载。

As long as a strong reference to the payload (the lock) exists, the strong reference to the mapped integer in keyCache avoids the removal of the payload from the lockCache cache.

这篇关于迭代WeakHashMap的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆