WeakHashMap和并发修改

时间:2018-01-23 21:24:57

标签: java multithreading concurrency garbage-collection

我正在阅读关于WeakHashMap的Java Doc,我得到了基本概念。 由于GC线程在后台运行,您可以获得“异常行为”,例如迭代时的ConcurrentModificationException等。

我没有得到的是,如果默认实现没有同步并且不以任何方式包含锁,那么为什么不存在获得不一致状态的可能性。 假设你有2个主题。 GC线程在某个索引处同时以相同的索引删除某个键,用户线程在数组中插入一个键值对。

对我来说,如果没有同步,那么获得不一致的哈希映射的风险很高。

更糟糕的是,做这样的事情实际上可能非常危险,因为v实际上可能是空的。

if (map.contains(k)) {
   V v = map.get(k)
}

我错过了什么吗?

4 个答案:

答案 0 :(得分:5)

您提到的不一致状态问题不会出现,因为GC不会主动重组WeakHashMaps。当垃圾收集器释放弱引用的引用时,相应的条目不会从映射中物理移除;条目只是陈旧,没有钥匙。在稍后的某个时间点,可以在地图上的某些其他操作期间物理地移除该条目,但GC不会承担该责任。

您可以在grepcode上看到此设计的一个Java版本实现。

答案 1 :(得分:0)

您所描述的内容是文档明确指出的内容:

  

因为垃圾收集器可能随时丢弃密钥,所以WeakHashMap可能表现得好像未知线程正在静默删除条目。

你唯一的错误就是假设你可以通过同步来保护状态。这不起作用,因为GC的同步不会是相互的。引用文档:

  

特别是,即使您在WeakHashMap实例上进行同步并且不调用其任何mutator方法,size方法也可能会随着时间的推移返回较小的值,{{1}返回isEmpty然后false的方法,true方法返回containsKey以及true以获取给定密钥的false }方法返回给定键的值,但稍后返回getnull方法返回putnull方法返回remove以前似乎出现在地图中的密钥,以及对密钥集,值集合和条目集的连续检查,以便连续生成较少数量的元素。

答案 2 :(得分:0)

  

此类主要用于密钥对象,其等于方法使用==运算符测试对象标识。一旦这样的密钥被丢弃,它就永远不会被重新创建,因此以后在WeakHashMap中查找该密钥是不可能的,并且对其条目已被删除感到惊讶。

因此,如果对.Activate基于身份检查的对象使用WeakHashMap,那么一切都很好。你提到的第一种情况(“一个GC线程在某个索引处删除某个键,同时在同一个索引处,用户线程在数组中插入一个键值对。”)是不可能的,因为只要用户thread保留对GC不能丢弃的密钥对象的引用。

第二个例子也是如此:

equals()

您保留引用if (map.contains(k)) { V v = map.get(k) } ,以便可以访问相应的对象,并且不能将其丢弃。

但是

  

这个类可以很好地处理等于的关键对象   方法不基于对象标识,例如String实例。   但是,使用这种可重新调用的关键对象,可以自动删除   可以证明其键被丢弃的WeakHashMap条目   混乱。

答案 3 :(得分:0)

参考

  

即使您在WeakHashMap [...]上进行同步,size方法也可能会随着时间的推移返回较小的值

javadoc向我充分解释说,存在一种不一致状态的可能性,并且它完全独立于同步。

稍后举几个例子,也提到了给定的例子:

  

for containsKey方法返回true,后来为false给定键

基本上,人们不应该依赖WeakHashMap的状态。但使用尽可能原子。因此,应该将给定的示例改为

V v = map.get(k);
if(null != v) {
}

Optional.ofNullable(map.get(k)).ifPresent(() -> { } );