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