我正在阅读关于WeakHashMap的Java Doc,我得到了基本概念。 由于GC线程在后台运行,您可以获得“异常行为”,例如迭代时的ConcurrentModificationException等。
我没有得到的是,如果默认实现没有同步并且不以任何方式包含锁,那么为什么不存在获得不一致状态的可能性。 假设你有2个主题。 GC线程在某个索引处同时以相同的索引删除某个键,用户线程在数组中插入一个键值对。
对我来说,如果没有同步,那么获得不一致的哈希映射的风险很高。
更糟糕的是,做这样的事情实际上可能非常危险,因为v实际上可能是空的。
if (map.contains(k)) {
V v = map.get(k)
}
我错过了什么吗?
答案 0 :(得分:5)
您提到的不一致状态问题不会出现,因为GC不会主动重组WeakHashMaps。当垃圾收集器释放弱引用的引用时,相应的条目不会从映射中物理移除;条目只是陈旧,没有钥匙。在稍后的某个时间点,可以在地图上的某些其他操作期间物理地移除该条目,但GC不会承担该责任。
您可以在grepcode上看到此设计的一个Java版本实现。
答案 1 :(得分:0)
您所描述的内容是文档明确指出的内容:
因为垃圾收集器可能随时丢弃密钥,所以
WeakHashMap
可能表现得好像未知线程正在静默删除条目。
你唯一的错误就是假设你可以通过同步来保护状态。这不起作用,因为GC的同步不会是相互的。引用文档:
特别是,即使您在
WeakHashMap
实例上进行同步并且不调用其任何mutator方法,size
方法也可能会随着时间的推移返回较小的值,{{1}返回isEmpty
然后false
的方法,true
方法返回containsKey
以及true
以获取给定密钥的false
}方法返回给定键的值,但稍后返回get
,null
方法返回put
,null
方法返回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(() -> { } );