我同时使用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缓存中删除有效负载.