java – 迭代WeakHashMap

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

当没有更多的线程需要ID X的资源时,可以删除key = X的Map中的Lock.然而,另一个线程可以进入,并尝试使用Map for 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<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缓存中删除有效负载.

相关文章

前言 逆向工程从数据库表直接生成代码,是日常开发中常用的敏...
前言 Java网络编程之Socket套接字,Socket套接字使用TCP提供...
前言 虽然现在已经很少项目会涉及GUI技术,但作为一个合格的...
前言 几乎所有的系统都有密码安全要求,这是基础的安全策略,...
前言 当我们在写设计文档,或者是其他涉及到数据架构、表结构...
前言 Fiddler是一款强大的Web调试代理工具,又称抓包软件,本...