【发布时间】:2015-01-19 11:11:06
【问题描述】:
我正在同时使用 WeakHashMap。我想基于一个 Integer 参数实现细粒度的锁定;如果线程 A 需要修改由 Integer a 标识的资源,而线程 B 对由 Integer b 标识的资源执行相同操作,则它们不需要同步。但是,如果有两个线程使用同一个资源,假设线程 C 也在使用由 Integer a 标识的资源,那么当然线程 A 和 C 需要在同一个 Lock 上同步。
当没有更多线程需要 ID X 的资源时,可以移除 Map 中 key=X 的锁。但是,此时另一个线程可能会进来并尝试使用 ID=X 的 Map 中的锁,因此我们在添加/删除锁时需要全局同步。 (这将是每个线程必须同步的唯一位置,无论 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
}
现在,Map 的内容可以在迭代时改变吗?我认为不会,因为通过调用mrLocks.keySet(),我为迭代范围创建了对所有键的强引用。对吗?
【问题讨论】:
-
我认为不是,来自JavaDoc:"集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。"
-
@m0skit0 啊,你可能是对的。返回的 Set 也将包含 WeakReference,但这就像 WeakHashMap 隐藏它一样被隐藏。所以我应该先克隆一个 keySet,然后迭代克隆,我猜,以确保我正在迭代一个具有强引用的集合。
-
也许我误解了你想要做的事情,但我并没有真正看到迭代的重要性。您要么找到条目,要么没有。 “但是,一个线程不知道何时移除锁,因为它不知道它是最后一个使用锁的线程......这就是我使用 WeakHashMap 的原因。”这里的逻辑跳转,我认为你解释得不是很好。您是关于 keySet 的问题,还是您要求一个不使用 WeakHashMap 的更好的设计? meta.stackexchange.com/questions/66377/what-is-the-xy-problem
-
@Radiodef 我希望在不再需要时删除地图条目。让最后一个线程执行此操作是不可能的,因此我通过使用 WeakHashMap 将内存管理委托给垃圾收集器。好吧,如果我使用 AtomicInteger 来跟踪使用锁的线程是可能的,但是有太多的簿记要做,这使得它变得一团糟。关键是我想避免条目永远在 Map 中,因为那会导致内存泄漏。
标签: java multithreading weak-references