首页 > 其他分享 >ConcurrentHashMap为何不能插入null?HashMap为何可以?

ConcurrentHashMap为何不能插入null?HashMap为何可以?

时间:2023-08-28 14:55:52浏览次数:42  
标签:map ConcurrentHashMap HashMap value 线程 key 为何 null

归纳来说就是两个问题:
1.ConcurrentHashMap 为什么 key 和 value 不能为 null?
2.ConcurrentHashMap 能保证复合操作的原子性吗?

1.ConcurrentHashMap 为什么 key 和 value 不能为 null?

ConcurrentHashMap 的 key 和 value 不能为 null 主要是为了避免二义性。null 是一个特殊的值,表示没有对象或没有引用。如果你用 null 作为键,那么你就无法区分这个键是否存在于 ConcurrentHashMap 中,还是根本没有这个键。同样,如果你用 null 作为值,那么你就无法区分这个值是否是真正存储在 ConcurrentHashMap 中的,还是因为找不到对应的键而返回的。

拿 get 方法取值来说,返回的结果为 null 存在两种情况:

值没有在集合中 ;
值本身就是 null。
这也就是二义性的由来。

 

多线程环境下,存在一个线程操作该 ConcurrentHashMap 时,其他的线程将该 ConcurrentHashMap 修改的情况,所以无法通过 containsKey(key) 来判断否存在这个键值对,也就没办法解决二义性问题了。

与此形成对比的是,HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个。如果传入 null 作为参数,就会返回 hash 值为 0 的位置的值。单线程环境下,不存在一个线程操作该 HashMap 时,其他的线程将该 HashMap 修改的情况,所以可以通过 contains(key)来做判断是否存在这个键值对,从而做相应的处理,也就不存在二义性问题。

也就是说,多线程下无法正确判定键值对是否存在(存在其他线程修改的情况),单线程是可以的(不存在其他线程修改的情况)。

如果你确实需要在 ConcurrentHashMap 中使用 null 的话,可以使用一个特殊的静态空对象来代替 null。

public static final Object NULL = new Object();

2.ConcurrentHashMap 能保证复合操作的原子性吗?

ConcurrentHashMap 是线程安全的,意味着它可以保证多个线程同时对它进行读写操作时,不会出现数据不一致的情况,也不会导致 JDK1.7 及之前版本的 HashMap 多线程操作导致死循环问题。但是,这并不意味着它可以保证所有的复合操作都是原子性的,一定不要搞混了!

复合操作是指由多个基本操作(如put、get、remove、containsKey等)组成的操作,例如先判断某个键是否存在containsKey(key),然后根据结果进行插入或更新put(key, value)。这种操作在执行过程中可能会被其他线程打断,导致结果不符合预期。

例如,有两个线程 A 和 B 同时对 ConcurrentHashMap 进行复合操作,如下:

// 线程 A
if (!map.containsKey(key)) {
map.put(key, value);
}
// 线程 B
if (!map.containsKey(key)) {
map.put(key, anotherValue);
}

如果线程 A 和 B 的执行顺序是这样:

线程 A 判断 map 中不存在 key
线程 B 判断 map 中不存在 key
线程 B 将 (key, anotherValue) 插入 map
线程 A 将 (key, value) 插入 map
那么最终的结果是 (key, value),而不是预期的 (key, anotherValue)。这就是复合操作的非原子性导致的问题。

那如何保证 ConcurrentHashMap 复合操作的原子性呢?

ConcurrentHashMap 提供了一些原子性的复合操作,如 putIfAbsent、compute、computeIfAbsent 、computeIfPresent、merge等。这些方法都可以接受一个函数作为参数,根据给定的 key 和 value 来计算一个新的 value,并且将其更新到 map 中。

上面的代码可以改写为:

// 线程 A
map.putIfAbsent(key, value);
// 线程 B
map.putIfAbsent(key, anotherValue);

或者:

// 线程 A
map.computeIfAbsent(key, k -> value);
// 线程 B
map.computeIfAbsent(key, k -> anotherValue);

此外,这种情况也能加锁同步呀!确实可以,但不建议使用加锁的同步机制,违背了使用 ConcurrentHashMap 的初衷。

在使用 ConcurrentHashMap 的时候,尽量使用这些原子性的复合操作方法来保证原子性。

标签:map,ConcurrentHashMap,HashMap,value,线程,key,为何,null
From: https://www.cnblogs.com/fulaien/p/17662268.html

相关文章

  • IM跨平台技术学习(八):新QQ桌面版为何选择Electron作为跨端框架
    本文由QQ技术团队王辉、吴浩、陈俊文分享,编辑Tina整理,本文收录时有内容修订和排版优化。1、引言在瞬息万变的互联网行业中,年过二十四的即时通讯IM应用QQ堪称超长寿的产品,见证了中国互联网崛起的完整历程。然而,如今这个元老级产品经历了一次从内到外彻底的重构。在这次重构......
  • [javase高级] HashMap实现原理
    HashMap是数组+链表实现的,既然用到hash散列,那么肯定不可避免的会出现冲突问题,HashMap解决冲突的方法是拉链法,因为这里有用到数组,那么当容量不足的时候就需要进行扩容操作了,在HashMap中有个术语叫冲突,当冲突几率越来越高的时候就需要进行扩容操作了那什么情况就叫冲突几率高呢?就是......
  • 【转载】如何通俗地解释欧拉角?之后为何要引入四元数?
    转载自:https://www.zhihu.com/tardis/bd/ans/236284413?source_id=1001   为何要引入四元数?首先是因为欧拉角有万向节死锁的问题。3D游戏或者3D电影中,比如黑客帝国中酷炫的旋转是怎么实现的?旋转的算法有很多,这里主要介绍其中一种:欧拉角。1欧拉角1.1欧拉角的算法......
  • 说一下 HashMap 的实现原理?
    HashMap是Java集合框架中的一个重要类,用于存储键值对。它的实现基于哈希表(HashTable)数据结构,其基本原理是通过将键映射到一个索引,然后在该索引位置存储对应的值。以下是HashMap的主要实现原理:哈希函数:当你向HashMap中添加键值对时,首先会将键通过哈希函数转换成一个整数,该......
  • 白银行情分析:为何贵金属成为避险资产首选?
    随着全球经济的不确定性和金融市场的波动,投资者对于保值和避险的需求越来越强烈。在众多避险资产中,贵金属,尤其是白银,逐渐成为投资者首选。本文将对贵金属作为避险资产首选的原因进行分析。一、稳定的价值储存功能作为贵金属之一,白银具有稳定的价值储存功能。与纸币不同,白银拥有实......
  • 为什么使用HashMap的键存的是自定义的键时需要重写hashcode和equals方法?
    当hashMap的键存的是自定义的键时需要重写对象的hashcode和equals方法存入的是对象时,应该hashMapd的键不能存储的值不能相同,如果重写方法的hashcode()方法,他会默认调用object类的hashcode方法,但是object的hashcode方法时地址值计算出来的hash值,并不是内容,这个时候就需要重......
  • AUC计算及为何不受样例不均衡的影响
    在很多排序场景下,尤其是当前许多数据集正负样例都不太均衡;或者说因训练集过大,可能会对数据进行负采样等操作。这擦操作的前提是建立在AUC值不会受到正负样本比例的影响。看过很多博客也都在讨论:为什么AUC不会受正负样例不平衡的影响?为什么排序喜欢选择AUC作为评判指标。一方面,从A......
  • ConcurrentHashMap 源码详解
    ConcurrentHashMap是Java提供的一个并发散列映射实现,它允许多个线程同时读写而不需要同步整个数据结构。它是线程安全的,并且相比于其他线程安全的Map实现(如Collections.synchronizedMap或Hashtable),它提供了更高的并发性能。以下是ConcurrentHashMap的一些核心特性和相应......
  • HashMap常见面试题
    HashMap的底层数据结构?JDK1.8之前HashMap底层是数组和链表结合在一起使用。HashMap通过key的hashCode经过扰动函数处理过后得到hash值,然后通过(n-1)&hash判断当前元素存放的位置(这里的n指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的......
  • HashMap常见面试题
    HashMap的底层数据结构?JDK1.8之前HashMap底层是数组和链表结合在一起使用。HashMap通过key的hashCode经过扰动函数处理过后得到hash值,然后通过(n-1)&hash判断当前元素存放的位置(这里的n指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素......