Ответ 1
Поскольку 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 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 завершить его, когда он больше не используется.
Edit:
После обсуждения полезной нагрузки в комментариях я подумал о решении с двумя кешами:
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 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
.