前言:

常见的关于HahsMap与ConcurrentHashMap的问题:

数据结构、线程安全、扩容、jdk1.7 HashMap死循环、jdk1.8 HashMap红黑树、容量必须是2的冥次

HashMap

数据结构:数组,单向链表

线程安全:不安全,HashTable线程安全,但是全用了 synchronized ,性能低

一、jdk1.7中

①:对HashMap初始化进行初始化;

初始化HashMap的 threshold 字段,通过因子算出,默认为0.75,算出来是16 * 0.75为12;

初始化hash种子信息;

保证了HashMap的容量是2的冥次。为什么HashMap的容量必须是2的冥次,因为可以增加有效长度,减少hash碰撞,关于hash碰撞:https://blog.csdn.net/qq_35583089/article/details/80048285

②:添加Entry对象,赋值

①:判断当前容量是否到达阈值,例如12

②:扩容

头插法:原先的头会成为新链表的尾部,原先的尾部会成为新链表的头

这里的代码就是为什么不建议并发时使用HashMap的原因,e.next形成死循环,导致CPU 100%卡死,并且线程不安全

二、jdk1.8中

jdk1.8中,当容量超过2的8次方(64)时,使用红黑树替代链表

ConcurrentHashMap

数据结构(jdk1.7):Segment数组,Segment下又有HashEntry,HashEntry为数组+链表

数据结构(jdk1.8):但是在jdk1.8中,起始的数据结构是数组+链表的。但是当单个链表长度

ConcurrentHashMap是一个线程安全的Map类,其通过多个Segment来保存数据,操作不同Segment之间是可以并发的,而操作统计个Segment进行数据的插入时,会进行 ReentrantLock 上锁操作

一、jdk1.7

先看看put方法

public V put(K key, V value) {
ConcurrentHashMap.Segment<K,V> s;
if (value == null)
throw new NullPointerException();
//通过key的hash值算出segment的下标
int hash = hash(key);
int j = (hash >>> segmentShift) & segmentMask;
if ((s = (ConcurrentHashMap.Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck
(segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment
//这里会判断对应下标的segment是否存在,不存在会进行初始化
s = ensureSegment(j);
return s.put(key, hash, value, false);
}

下面是Segment.put方法

final V put(K key, int hash, V value, boolean onlyIfAbsent) {
   //尝试获得锁,并开始同步
ConcurrentHashMap.HashEntry<K,V> node = tryLock() ? null :
scanAndLockForPut(key, hash, value);
V oldValue;
try {
ConcurrentHashMap.HashEntry<K,V>[] tab = table;
int index = (tab.length - 1) & hash;
ConcurrentHashMap.HashEntry<K,V> first = entryAt(tab, index);
for (ConcurrentHashMap.HashEntry<K,V> e = first;;) {
if (e != null) {
K k;
if ((k = e.key) == key ||
(e.hash == hash && key.equals(k))) {
oldValue = e.value;
if (!onlyIfAbsent) {
e.value = value;
++modCount;
}
break;
}
e = e.next;
}
else {
         //和HashMap一样,链表使用头插法,后插入的元素永远在数组的最前面
if (node != null)
node.setNext(first);
else
node = new ConcurrentHashMap.HashEntry<K,V>(hash, key, value, first);
int c = count + 1;
          //超过Segment阈值时,对HashEntry进行扩容
if (c > threshold && tab.length < MAXIMUM_CAPACITY)
rehash(node);
else
setEntryAt(tab, index, node);
++modCount;
count = c;
oldValue = null;
break;
}
}
} finally {
unlock();
}
return oldValue;
}

二、jdk1.8

java1.7采用volatile去获取已存在的Segment。java1.8采用的CAS算法,而没用使用Segment进行数据存储

HashMap ConcurrentHashMap解读的更多相关文章

  1. HashTable & HashMap & ConcurrentHashMap 原理与区别

    一.三者的区别     HashTable HashMap ConcurrentHashMap 底层数据结构 数组+链表 数组+链表 数组+链表 key可为空 否 是 否 value可为空 否 是 否 ...

  2. 深入理解HashMap+ConcurrentHashMap的扩容策略

    前言 理解HashMap和ConcurrentHashMap的重点在于: (1)理解HashMap的数据结构的设计和实现思路 (2)在(1)的基础上,理解ConcurrentHashMap的并发安全的 ...

  3. Jdk8 Hashmap ConcurrentHashMap

    JDK1.8 Hashmap JDK1.8 ConcurrentHashMap 不采用segment而采用 synchronized (f)  f = table[i]; 减小锁的力度 设计了MOVE ...

  4. java多线程之hashmap concurrenthashmap的状态同步

    最近在高并发的系统中发现,concurrenthashmap除了大家熟知的避免循环期间发生ConcurrentModificationException异常外,还有重要的一点是Retrievals r ...

  5. HashMap完全解读

    一.什么是HashMap 基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了非同步和允许使用 null 之外,HashMap 类与 Has ...

  6. HashMap? ConcurrentHashMap? 相信看完这篇没人能难住你!

    前言 Map 这样的 Key Value 在软件开发中是非常经典的结构,常用于在内存中存放数据. 本篇主要想讨论 ConcurrentHashMap 这样一个并发容器,在正式开始之前我觉得有必要谈谈 ...

  7. ConcurrentHashMap 解读

    初始化: 问题:如何当且仅只有一个线程初始化table private final Node<K,V>[] initTable() { Node<K,V>[] tab; int ...

  8. Java7/8 HashMap ConcurrentHashMap

    网上关于 HashMap 和 ConcurrentHashMap 的文章确实不少,不过缺斤少两的文章比较多,所以才想自己也写一篇,把细节说清楚说透,尤其像 Java8 中的 ConcurrentHas ...

  9. HashMap? ConcurrentHashMap?

    前言 Map 这样的 Key Value 在软件开发中是非常经典的结构,常用于在内存中存放数据. 本篇主要想讨论 ConcurrentHashMap 这样一个并发容器,在正式开始之前我觉得有必要谈谈 ...

随机推荐

  1. 第七届蓝桥杯javaB组真题解析-方格填数(第六题)

    题目 /* 方格填数 如下的10个格子 +--+--+--+ | | | | +--+--+--+--+ | | | | | +--+--+--+--+ | | | | +--+--+--+ (如果显 ...

  2. Go文档:go命令

    目录 go go bug--启动bug报告 go build--编译包及其依赖包 go clean--删除对象文件和缓存文件 go doc--查看包或符号的文档 go env--打印环境变量 go f ...

  3. Java基础知识笔记第三章:运算符表达式语句

    算术运算符与表达式 操作符 描述 例子 + 加法 - 相加运算符两侧的值 A + B 等于 30 - 减法 - 左操作数减去右操作数 A – B 等于 -10 * 乘法 - 相乘操作符两侧的值 A * ...

  4. frp 配置

    前言 对于没有公网 IP 的内网用户来说,远程管理或在外网访问内网机器上的服务是一个问题. 今天给大家介绍一款好用内网穿透工具 FRP,FRP 全名:Fast Reverse Proxy.FRP 是一 ...

  5. Python中的进制表示方式及转换方法

    在Python中,非十进制数字的表示方式为: 二进制:前面加0b,如0b1001 八进制:前面加0o,如0o3562 十六进制:前面加0x,如0x2af3 不同进制数字可直接进行数学计算,结果返回十进 ...

  6. overlay rate

    1.导入nii.img文件,三维矩阵 2.模版矩阵和网络矩阵对应位置元素相乘 .* 3.生成位置为0的新矩阵 cc=(nii_new==0); 4.两个矩阵的非零元素个数   t1=length(ni ...

  7. Kubernetes 1.17.2 高可用部署

    20.0.0.200    10.0.0.200 bs-k8s-master01 管理节点 2c2g 20.0.0.201    10.0.0.201 bs-k8s-master02 管理节点 2c2 ...

  8. 设计模式课程 设计模式精讲 21-2 观察者模式coding

    1 代码演练 1.1 代码演练1(一对一观察) 1.2 代码演练2(一对多观察) 1.3 代码演练3(多对多观察) 1 代码演练 1.1 代码演练1(一对一观察) 需求: 木木网课程系统,教师后台提醒 ...

  9. 键盘类型UIKeyboardType

    UITextField.UITextView等能够调出系统键盘的控件,通过下面这个属性可以控制弹出键盘的样式: self.priceTextField.keyboardType = UIKeyboar ...

  10. 数组NSArray与NSMutableArray的常用方法

    数组中可以放任何类型的数据,并且一个数组中的元素类型可以不一致.只要是(id类型)对象. NSArray 1.初始化 NSArray *array = @[]; 2.初始化,最后需要以nil结尾 NS ...