浅谈HashMap, HashTable, ConcurrentHashMap 之间的区别--Java

一、HashMap

HashMap的主干是一个Entry数组。Entry是HashMap的基本组成单元,每一个Entry包含一个key-value键值对。(其实所谓Map其实就是保存了两个对象之间的映射关系的一种集合)

HashMap本身不是线程安全的,key值允许为空.
所以在多线程环境下,使用哈希表时可以使用:

  • HashTable
  • ConcurrentHashMap

下面将谈一谈这两种哈希表的区别

二、HashTable

Hashtable: 线程安全. 使用 synchronized 锁 Hashtable 对象, 效率较低. key 不允许为 null.
HashTable只是在HashMap的基础上简单的把关键方法加上了synchronized关键字。
在这里插入图片描述

这相当于直接针对HashTable对象本身加锁了

  • 如果多线程访问同一个 Hashtable 就会直接造成锁冲突。
  • size 属性也是通过 synchronized 来控制同步, 也是比较慢的。
  • 一旦触发扩容, 就由该线程完成整个扩容过程. 这个过程会涉及到大量的元素拷贝, 效率会非常低。

一个HashTable只有一把锁,两个线程访问HashTable中的任何数据都会出现锁竞争,严重影响了读写的效率

三、ConcurrentHashMap

ConcurrentHashMap: 线程安全. 使用 synchronized 锁每个链表头结点, 锁冲突概率低, 充分利用CAS 机制. 优化了扩容方式. key 不允许为 null.

ConcurrentHashMap在HashTable的基础上又做出了一系列的 改进和优化,以Java108为例

1、 读操作没有加锁(但是使用了 volatile 保证从内存读取结果), 只对写操作进行加锁. 加锁的方式仍然是使用 synchronized, 但是不是锁整个对象, 而是 “锁桶” (用每个链表的头结点作为锁对象), 大大降低了锁冲突的概率
2、充分利用 CAS 特性. 比如 size 属性通过 CAS 来更新. 避免出现重量级锁的情况
3、优化了扩容方式: 化整为零

  • 发现需要扩容的线程, 只需要创建一个新的数组, 同时只搬几个元素过去。
  • 扩容期间, 新老数组同时存在。
  • 后续每个来操作 ConcurrentHashMap 的线程, 都会参与搬家的过程. 每个操作负责搬运一小部分元素。
  • 搬完最后一个元素再把老数组删掉。
  • 这个期间, 插入只往新数组加。
  • 这个期间, 查找需要同时查新数组和老数组。

ConcurrentHashMap 每个哈希桶都有一把锁,只有两个线程访问的恰好是同一个哈希桶上的数据才会出现锁冲突。

四、总结三者区别:

HashMap: 线程不安全. key 允许为 null
Hashtable: 线程安全. 使用 synchronized 锁 Hashtable 对象, 效率较低. key 不允许为 null.
ConcurrentHashMap: 线程安全. 使用 synchronized 锁每个链表头结点, 锁冲突概率低, 充分利用CAS 机制. 优化了扩容方式. key 不允许为 null

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值