java map确保多线程安全_让我们来看看,多线程下的Map是如何实现线程安全的

本文深入探讨了多线程环境下Java Map的线程安全问题,分析了HashTable、Collections.synchronizedMap(Map)和ConcurrentHashMap的实现原理。HashTable虽然线程安全但效率低,ConcurrentHashMap通过分段锁或CAS保证并发性能,而Collections.synchronizedMap提供了一种灵活的线程安全选项。文章详细阐述了各个实现的优缺点,如HashTable的key-value不允许为NULL,ConcurrentHashMap在JDK1.7和1.8的不同实现策略,以及CAS算法在其中的作用。
摘要由CSDN通过智能技术生成

多线程下的Map集合

前语

上一篇文章<>是在大考周前写的有关HashMap的文章,在其开头开头提到过ConcurrentHashMap和HashTable,因为既然讲到了Map那么就绕不开,HashMap、HashTable、ConcurrentHashMap这三兄弟,先简单介绍一下这两个新朋友:HashTable是遗留类,ConcurrentHashMap类是有点高级的,说实话在写这篇文章前,这两个类我是没用过,不是说它不重要,只能说我层次还没到。在阅读本篇文章时,我强烈建议大家先去看看<>

背景

HashMap在多线程环境下是不安全的,jdk1.7中是因为采用的是头插法,在多线程环境下两个线程同时扩容时会出现环链导致死循环;而jdk1.8中改用尾插法,避免了这个情况,但是其put操作在多线程环境下会发生覆盖,导致线程不安全。那在多线程环境下我们应该怎么做呢?

  • 使用HashTable类
  • 使用Collections.synchronizedMap(Map)创建线程安全的map
  • 使用ConcurrentHashMap类

那么接下来我们就来细聊一下这三种方式:

HashTable

HashTable是遗留类,遗留类是指在jdk的集合框架出现前,就已经存在了,并且所有遗留类都重新设计为支持JDK5中的通用类。HashTable的很多功能都与HashMap类似,那么通过查看源代码可以发现,说HashTable是线程安全的是因为HashTable使用了synchronized关键字来保证线程同步。d93ab153fee2810edd72df1f8ce2d3cb.png

我们都知道使用synchronized属于独占式的悲观锁,加上的是重量级锁,当一个线程访问HashTable的同步方法时候,其它的线程只能是阻塞或轮询状态,所以HashTable的并发性是比较差的,效率比较低。翻阅源码还可以得到以下结论:

  1. HashTable的默认容量为11,HashMap为16,而且HashTable不要求底层数组容量一定为2的整数次幂,HashMap则要求一定为2的整数次幂!
  2. HashTable没有使用红黑树,无论多大,一直是采用尾插的链表
  3. HashTable扩容时容量为原来的2倍+1,而HashMap为原来的二倍
  4. HashTable直接通过对象的hashCode()方法来获得哈希值,而HashMap中是通过HashCode方法算出的Hash值,然后与其高十六位做异或运算,得到最终的哈希值。
  5. HashTable不允许key和value为NULL。

为什么HashTable的key-value不能为NULL?

在源码注释中有这样一句话:

615b0d6ac251135c92446ce0a9071fa9.png

大致意思是要从Hashtable成功存储和检索对象,用作键key的对象必须实现hashCode方法和equals方法,显然我们的NULL是不具备这两个方法的。

而value值不为空具体的表现为在其源码中有一个判空处理,为空抛出NullPointerException异常。

4ade67f97d054503bfec845c69a13833.png

那么为什么要对value加上这样一个判空处理,来确保value值不为空呢? 我们现在假设允许key-value为null,那么我们使用map.get(key)时,如果返回的结果为null,那么对应的key到底是不存在,还是其value为null呢?在单线程的情况下我们当然可以通过调用map.containsKey(key)来确定key是否存在,而在多线程情况下,为了保证contains和get操作的原子性,显然这种做法在多线程的情况下我们是无法使用的。

Collections.synchronizedMap(Map)

HashTable由jdk1.1中引入,它那种直接采用Synchronized关键字来修饰方法的暴力操作,在很大程度上限制了其拓展性,因此在jdk1.2中引入了Collections.synchronizedMap(Map),这种相对较灵活的方式来保证Map的线程安全。那么让我们来看看它的源码:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值