Java – iterative weakhashmap
I also use weakhashmap I want to implement fine-grained locking based on an integer parameter; If thread a needs to modify the resource identified by integer a and thread B performs the same operation on the resource identified by integer B, synchronization is not required However, if two threads use the same resource, thread C also uses the resource identified by the integer A. of course, threads a and C need to synchronize on the same lock
When no more threads need resources with ID x, you can delete lock in the map with key = X However, another thread can enter and try to use the lock in map for id = x, so global synchronization is required when adding / removing locks (this will be the only place where each thread must synchronize, regardless of the integer parameter) however, a thread does not know when to delete the lock because it does not know that it is the last thread to use the lock
That's why I use weakhashmap: when ID is no longer used, key value pairs can be deleted when GC needs them
In order to ensure that I have strongly referenced the keywords of existing entries and the object references that form the mapping keywords, 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
}
Now, can the contents of the map be changed during iteration? I don't think so, because by calling mrlocks Keyset (), I created a powerful iteration scope that references all keys Is that right?
Solution
Since the API has no assertion about keyset (), I recommend using the following cache:
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;
}
In this way, you reference the monitor (the key itself) when it is locked and allow the GC to complete it when it is no longer in use
Editor: after the discussion on payload, I came up with 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;
}
As long as there is a strong reference to the payload (lock), a strong reference to the mapped integer in the keycache will avoid deleting the payload from the lockcache cache cache
