我正在使用WeaekHashMap
来实现缓存。我想知道我是否正在迭代这张地图的键,同时垃圾收集器正在主动从这张地图中删除键,我会收到ConcurrentModificationException
吗?
我不这么认为,因为据我所知,并发修改是因为应用程序代码中的错误导致开发人员忘记了解相同的映射是由其他线程共享/使用的,在这种情况下,它不应该发生。但是想知道当WeakHashMap未同步时JVM会如何处理?
答案 0 :(得分:12)
正如bkail所说,当GC“删除”来自WeakHashMap
的条目时,它不会导致并发修改。实际上,GC通过对WeakReference
对象(包含真实密钥)本身的硬引用来收集底层对象。因此,不会收集由映射直接引用的真实对象(引用对象),因此在您的某个线程调用此映射中的方法之前,映射不会更改。此时,映射将检查GC中的引用队列,并查找已收集的所有键并将其从映射中删除 - 因此对映射结构的实际更改将发生在您的一个线程上。
在考虑这个问题时,可能会出现这样一种情况,即您可能会在这样的地图中进行并发修改而不会出现在另一种地图中 - 如果您放置了已存在的密钥或调用了getter方法。但实际上,在并发应用程序中,您应该锁定这些调用,这样您的程序中就会出现真正的并发访问错误。
在回答您的问题时,您确实不应该使用 WeakHashMap
作为缓存(即使您正在讨论缓存密钥)。在缓存中,当不再引用值时,您不希望您的值“神奇地”消失。通常,当有一定的最大数量(类似于Apache集合LRUMap
)或在内存需求上释放时,您希望它们消失。
对于后者,您可以使用带有SoftReference
的映射(Apache集合提供ReferenceMap
,允许您指定键或值的引用类型)。指定软引用仅基于内存压力释放 - 另一方面弱引用必须通过GC识别出对象没有剩余硬引用并且可以随时释放它。当然,软引用如何真正起作用也取决于JVM的实现。
编辑:我重新阅读了您的问题并希望解决另一个问题。因为实际修改发生在您自己的线程上的WeakHashMap
内部结构,如果您只在单个线程中使用此映射,则不需要同步任何方法调用。此行为与任何其他Map
没有什么不同。
答案 1 :(得分:5)
不,您不会收到ConcurrentModificationException。当您调用各种操作时,WeakHashMap使用ReferenceQueue.poll。换句话说,每个调用者都默默地负责清除Map中的陈旧条目。但是,这确实意味着从多个线程调用WeakHashMap上的方法是不安全的,否则这些线程似乎是“只读”,因为对get()的任何调用都可以破坏另一个线程试图迭代的条目链表
答案 2 :(得分:0)
WeakHashMap对键而不是值很弱,因此如果要在未使用值时释放空间,则它不适合值的缓存。您可能需要查看MapMaker中的google collections。
答案 3 :(得分:-2)
文档在这方面并不十分清楚,但确实如此:
WeakHashMap类的行为 部分取决于的行为 垃圾收集器,所以几个 熟悉(虽然不是必需的)地图 不变量不适用于此类。 因为垃圾收集器可能 随时丢弃密钥,a WeakHashMap可能表现得像 未知的线程正在默默地删除 条目即可。特别是,即使你 在WeakHashMap实例上同步 并且不调用任何mutator 方法,有可能的大小 返回较小值的方法 时间,为isEmpty方法返回 假的,然后是真的,因为 containsKey方法返回true和 对于给定的密钥,后来为false get方法返回一个值 给定键但稍后返回null,for put方法返回null和 remove方法为a返回false 以前似乎出现的关键 地图,并连续 考试的关键,价值 设置,并将条目设置为yield 相继较少的数量 元素。 - Java API
我认为,鉴于该描述,您应该在迭代地图时偶尔收到ConcurrentModificationException
s。我会设计你的缓存,所以你尽可能少地迭代。