Java hashmap在多线程环境中可以有重复的键吗
如果我们不使用Collections.synchronizedMap(),假设我有一个多线程环境 我知道比赛情况、重新调整尺寸等问题 我的问题是,是否有一个案例2线程Java hashmap在多线程环境中可以有重复的键吗,java,hash,collections,hashmap,Java,Hash,Collections,Hashmap,如果我们不使用Collections.synchronizedMap(),假设我有一个多线程环境 我知道比赛情况、重新调整尺寸等问题 我的问题是,是否有一个案例2线程Ta和Tb具有相同的对象并试图放入地图中 如果不是如何防止,是否可能有2个条目。同时运行的两个不同线程的两个put调用之间是否存在时间差的一小部分 根据我的理解,因为Ta和Tb都将在放置前进行检查,因此此处可能存在重复钥匙的情况 考虑到我们已经正确地覆盖了hashcode和equals。的Javadoc forHashMap声明:
Ta
和Tb
具有相同的对象并试图放入地图中
如果不是如何防止,是否可能有2个条目。同时运行的两个不同线程的两个put调用之间是否存在时间差的一小部分
根据我的理解,因为Ta
和Tb
都将在放置前进行检查,因此此处可能存在重复钥匙的情况
考虑到我们已经正确地覆盖了
hashcode
和equals
。的Javadoc forHashMap
声明:
请注意,此实现不同步。如果有多个线程
同时访问哈希映射,并至少访问其中一个线程
如果从结构上修改贴图,则必须从外部对其进行同步。(一)
结构修改是添加或删除一个或多个结构的任何操作
更多映射;只需更改与
实例已包含的不是结构修改。)这是
通常通过在某些对象上进行同步来完成
封装地图。如果不存在这样的对象,则应删除地图
使用Collections.synchronizedMap方法“包装”
因此,文档说您必须以某种方式同步访问,但没有说明如果不同步会发生什么。这意味着当你这样做的时候,行为是未定义的——所有的赌注都是无效的
你可以看看你自己。put
的核心是:
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
for(条目e=table[i];e!=null;e=e.next){
对象k;
如果(e.hash==hash&((k=e.key)==key | | key.equals(k))){
V oldValue=e.value;
e、 价值=价值;
e、 记录存取(本);
返回旧值;
}
}
modCount++;
加法器(散列、键、值、i);
返回null;
(编辑-这是Java 6中的实现。Java 8的实现有很大的不同——这加强了这一点)
如果两个线程同时尝试这样做,我们可以推测结果——但这很难推理。有时它会导致两个条目具有相同的密钥,有时则不会。这取决于时间
TreeMap
的put()
当然是完全不同的,以这种方式滥用它的怪癖也会有所不同
任何这样的行为都是实现的一个怪癖,将来实现可能会在没有警告的情况下更改,因为我们讨论的是未定义的行为。实施不会向您承诺:
- 静默删除条目
- 进入无限循环
NullPointerException
- 声称拥有大量内存
- 损坏存储区,使具有其他密钥的条目丢失
- 使以前删除的条目重新出现
- 从堆内存创建包含垃圾的条目
- 等等
迭代器对对象进行操作时,来自其他地方的修改将导致迭代器抛出ConcurrentModificationException
——但这与同步不同,如果使用SynchronizedMap
总之,不要这样做。Javadoc forHashMap
声明:
请注意,此实现不同步。如果有多个线程
同时访问哈希映射,并至少访问其中一个线程
如果从结构上修改贴图,则必须从外部对其进行同步。(一)
结构修改是添加或删除一个或多个结构的任何操作
更多映射;只需更改与
实例已包含的不是结构修改。)这是
通常通过在某些对象上进行同步来完成
封装地图。如果不存在这样的对象,则应删除地图
使用Collections.synchronizedMap方法“包装”
因此,文档说您必须以某种方式同步访问,但没有说明如果不同步会发生什么。这意味着当你这样做的时候,行为是未定义的——所有的赌注都是无效的
你可以看看你自己。put
的核心是:
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
for(条目e=table[i];e!=null;e=e.next){
对象k;
如果(e.hash==hash&((k=e.key)==key | | key.equals(k))){
V oldValue=e.value;
e、 价值=价值;
e、 记录存取(本);
返回旧值;
}
}
modCount++;
加法器(散列、键、值、i);
返回null;
(编辑-这是Java 6中的实现。Java 8的实现有很大的不同——这加强了这一点)
如果两个线程同时尝试这样做,我们可以推测结果——但这很难推理。有时它会导致两个条目具有相同的密钥,有时则不会。这取决于时间
TreeMap
的put()
当然是完全不同的,以这种方式滥用它的怪癖也会有所不同
任何这样的行为都是实现的一个怪癖,将来实现可能会在没有警告的情况下更改,因为我们讨论的是未定义的行为。实施不会向您承诺:
- 静默删除条目
- 进入无限循环
NullPointerException
- 声称拥有大量内存
- 损坏存储区,使具有其他密钥的条目丢失
- 使以前删除的条目重新出现
- 从堆内存创建包含垃圾的条目
- 等等