我正在使用WeaekHashMap来实现缓存。我在想,如果我正在迭代这个映射的键,同时垃圾收集器正在积极地从这个映射中删除键,我会收到一个ConcurrentModificationException吗?我不这么认为,因为据我所知,并发修改异常的发生是由于应用程序代码中的错误,开发人员忘记了相同的map被其他线程共享/使用,在这种情况下,这种情况不应该发生。但是想知道当WeakHashMap不同步时,JVM将如何处理这个问题呢?
发布于 2010-05-19 08:51:20
正如bkail所说,当GC从WeakHashMap中“移除”一个条目时,它不会导致并发修改。实际上,GC通过对WeakReference对象(持有真正的键)本身的硬引用来收集底层对象。因此,映射直接引用的真实对象(引用对象)不会被收集,因此映射不会更改,直到您的某个线程调用此映射中的方法。此时,map检查来自GC的引用队列,并找到所有已收集的键,并将它们从map中删除-因此,对map结构的实际更改发生在您的一个线程上。
考虑到这一点,可能会有一种情况,在这样的映射中,您可能会得到一个并发修改,而不会在另一种映射中获得-如果您放置了一个已经存在的键或调用了一个getter方法。但实际上,在并发应用程序中,无论如何你都应该锁定这些调用,这样你的程序中就会有一个真正的并发访问错误。
也就是说,在回答您的问题时,您确实不应该使用 WeakHashMap作为缓存(即使您正在谈论缓存键)。在缓存中,当值不再被引用时,您不希望值“神奇地”消失。通常,您希望它们在达到一定的最大数量(例如Apache集合LRUMap)或按内存需求释放时消失。
对于后者,您可以使用带有SoftReference的映射(Apache集合提供了一个ReferenceMap,允许您指定键或值的引用类型)。软引用被指定为仅基于内存压力来释放-另一方面,弱引用必须更多地处理GC,因为GC认识到对象没有硬引用,可以随时释放它。当然,软引用的实际工作方式也取决于JVM实现。
EDIT:我重读了你的问题,并想解决另一个问题。因为实际的修改发生在您自己的线程上的WeakHashMap内部结构上,所以如果您只在单个线程中使用此映射,则不需要同步任何方法调用。此行为与任何其他Map没有什么不同。
发布于 2010-05-19 08:13:06
不,您不会收到ConcurrentModificationException。当你调用各种操作时,WeakHashMap会使用ReferenceQueue.poll。换句话说,每个调用者都要默默地负责清除Map中的陈旧条目。但是,这确实意味着从多个线程调用WeakHashMap上的方法是不安全的,否则这些方法看起来是“只读”的,因为对get()的任何调用都会破坏另一个线程试图迭代的条目链表。
发布于 2010-05-19 05:57:58
WeakHashMap在键而不是值上很弱,所以如果您想在不使用值的时候释放空间,那么它不适合值的缓存。您可能希望从google collections查看一下MapMaker。
https://stackoverflow.com/questions/2861410
复制相似问题