java 并发容器一之ConcurrentHashMap(基于JDK1.8)
上一篇文章简单的写了一下,BoundedConcurrentHashMap,觉得https://www.cnblogs.com/qiaoyutao/p/10903813.html用的并不多;今天着重写一下ConcurrentHashMap几个常用方法的源码理解,
注: 本文章纯属个人理解,对阅读者只作参考。
先来看构造器
public ConcurrentHashMap(Map<? extends K, ? extends V> m) { this.sizeCtl = DEFAULT_CAPACITY; // 将表的长度设置为默认的DEFAULT_CAPCITY=16; putAll(m); // 调用方法将传进来的map构建成一个线程安全的hash表,下面是putAll的源码 }
public void putAll(Map<? extends K, ? extends V> m) {
tryPresize(m.size()); // 根据传入的map表的大小来尝试调整此map表的sizeCtl的大小,并创建一个m.size长度的Node<k,v>[]数组给成员变量table。
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) // 遍历集合放入上面的Node数组中。 下面是putVal方法
putVal(e.getKey(), e.getValue(), false);
}
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException(); // 不允许key或者value为null
int hash = spread(key.hashCode()); // 对key进行再hash
int binCount = 0; // 用来计算在这个结点总共有多少个元素, 以此来判断是否需要扩容或者转化为红黑树
for (Node<K,V>[] tab = table;;) { // 此处将Node数组table赋值给tab
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0) // 如果tab也就是Node数组为空那么就进行初始化
tab = initTable(); // initTable方法源码在下面
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { // tabAt用来取出tab中(n-1)&hash 下标下的Node结点
if (casTabAt(tab, i, null, // 如果i对应的位置若为null 那么就用cas的方式将此方法的第三个参数添加进来。 这里是通过cas操作的没有加锁
new Node<K,V>(hash, key, value, null))) // 这里的参数null代表新创建的Node的下一个元素为null
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED) /// 当有结点的hash值等于MOVED时,说明该Node数组结点下的hash表正在扩容,那么就帮助扩容(多线程扩容,helpTransfer方法就是帮助扩容)
tab = helpTransfer(tab, f);
else {
// 进入else 说明该key下有结点,那么就加锁进行操作,如果fh也即是该结点的hash如果大于0说明是一个链表,就循环遍历这个链表并比较key,若期间有key相等的就将value替换,若没有就新创建一个Node节点添加到
// 链表结尾;否则就判断该Node节点是不是TreeBin(红黑树)类型的,就调用putTreeVal将值替换或添加进红黑树中, putTreeVal方法见下面。 当一个结点下的数量超过8时,就将此Hash表转换为红黑树
V oldVal = null;
synchronized (f) {// 对该Node结点进行加锁
if (tabAt(tab, i) == f) { // 取出该结点与前面if中取出的结点比较,如果相同那么说明期间没有别的线程进行操作该结点。
if (fh >= 0) { // 该结点的hash大于0时说明是链表
binCount = 1;
for (Node<K,V> e = f;; ++binCount) { // 遍历该结点下的hash表
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) { // 比较key和key的hash是否相同,如果相同那么就替换value
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) { // 如果没有key相同的Node结点,那么就创建一个并添加到hash表的结尾
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
else if (f instanceof TreeBin) { // 如果该Node数组结点下的Node结点是TreeBin类型的,说明是一个红黑树,就
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD) // 当binCount(结点下的hash表的元素数量大于等于TREEIFY_THRESHOLD(8)时,就转换为红黑树)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) { // 判断Node数组是否为空,不为空的话说明hash表已经初始化。
if ((sc = sizeCtl) < 0) // 当前sizeCtl < 0时,说明该数组正在被别的线程操作(或者扩容,或者初始化), 那么就用Thread.yield挂起线程
Thread.yield(); // lost initialization race; just spin
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
/** U.compareAndSwapInt(this,SIZECTL,sc,-1) 是java的Unsafe类的方法,使用了CAS算法来保证操作的原子性;何为CAS也即比较并替换,这里只大概的讲解,具体请参考https://www.jianshu.com/p/fb6e91b013cc
* CAS是根据内存地址来判断值有没有被修改, 于是this:表示被操作的对象,SIZECTL:是当前的sizeCtl的值,sc是sizeCtl的旧值,-1是即将要替换成的值。 compareAndSwapInt先会比较SIZECTL的值与sizeCtl是否相同如果
* 相同说明在期间没有操作hash表,就把当前sizeCtl的值改为-1,并返回true,告诉其他线程,有线程正在初始化hash表。如果不相同说明有线程操作正在初始化hash表,其他线程就会等待挂起。
try {
if ((tab = table) == null || tab.length == 0) { // 如果Node数组还没有初始化
int n = (sc > 0) ? sc : DEFAULT_CAPACITY; // 确定Node数组的大小。
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n]; // 创建数组
table = tab = nt; // 将数组给table和tab,给tab的目的是告诉其他线程,hash表已经初始化。
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc; // 将数组长度无符号右移位2的次幂更新到sizeCtl
}
break;
}
}
return tab;
}
其他的put方法也都是调用了pubVal(), 至于get等方法就不用写了,也就是比较key取value。
java 并发容器一之ConcurrentHashMap(基于JDK1.8)的更多相关文章
- java 并发容器一之BoundedConcurrentHashMap(基于JDK1.8)
最近开始学习java并发容器,以补充自己在并发方面的知识,从源码上进行.如有不正确之处,还请各位大神批评指正. 前言: 本人个人理解,看一个类的源码要先从构造器入手,然后再看方法.下面看Bounded ...
- Java并发编程总结4——ConcurrentHashMap在jdk1.8中的改进(转)
一.简单回顾ConcurrentHashMap在jdk1.7中的设计 先简单看下ConcurrentHashMap类在jdk1.7中的设计,其基本结构如图所示: 每一个segment都是一个HashE ...
- Java并发编程总结4——ConcurrentHashMap在jdk1.8中的改进
一.简单回顾ConcurrentHashMap在jdk1.7中的设计 先简单看下ConcurrentHashMap类在jdk1.7中的设计,其基本结构如图所示: 每一个segment都是一个HashE ...
- Java并发容器——ConcurrentSkipListMap和ConcurrentHashMap
一:ConcurrentSkipListMap TreeMap使用红黑树按照key的顺序(自然顺序.自定义顺序)来使得键值对有序存储,但是只能在单线程下安全使用:多线程下想要使键值对按照key的顺序来 ...
- Java并发编程系列-(5) Java并发容器
5 并发容器 5.1 Hashtable.HashMap.TreeMap.HashSet.LinkedHashMap 在介绍并发容器之前,先分析下普通的容器,以及相应的实现,方便后续的对比. Hash ...
- Java 并发系列之六:java 并发容器(4个)
1. ConcurrentHashMap 2. ConcurrentLinkedQueue 3. ConcurrentSkipListMap 4. ConcurrentSkipListSet 5. t ...
- Java并发指南14:Java并发容器ConcurrentSkipListMap与CopyOnWriteArrayList
原文出处http://cmsblogs.com/ 『chenssy』 到目前为止,我们在Java世界里看到了两种实现key-value的数据结构:Hash.TreeMap,这两种数据结构各自都有着优缺 ...
- 《Java并发编程的艺术》第6/7/8章 Java并发容器与框架/13个原子操作/并发工具类
第6章 Java并发容器和框架 6.1 ConcurrentHashMap(线程安全的HashMap.锁分段技术) 6.1.1 为什么要使用ConcurrentHashMap 在并发编程中使用Has ...
- 【java并发容器】并发容器之CopyOnWriteArrayList
原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容 ...
随机推荐
- hdu-5003 Osu!(水题)
题目链接: Osu! time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Prob ...
- mysql 数据库电脑间迁移
应用实例: database1(简称DB1)保存在PC1中的MySQL中,需要将DB1迁移到PC2中的MySQL中 环境: win7 MySQL5.7.13 参考: http://stackoverf ...
- 【CQ18高一暑假前挑战赛4】标程
[二分或者STL] 二分: #include<bits/stdc++.h> using namespace std; ; int a[maxn]; int main() { ,pos; s ...
- OTL之Oracle开发总结《转》
OTL之Oracle开发总结---转 关 于OTL,网上介绍的也不少,但看来看去也只是官方的那些文档.OTL很好用,结合官方提供的一些例子,多多尝试才能领悟.经过一个月左右的项目开发,对 OTL也 ...
- 使用IIS Express调试网站的方法
如果不想安装IIS,可以直接使用IIS Express来运行网站. vs2012: 新建个文档,拷贝下面代码 taskkill /F /IM "WebDev.WebServer40.EXE& ...
- <正则吃饺子>:关于集合的简单整理总结
项目中用到的集合不可谓不多,对于自己的一次面试,要求说下自己用过的集合,自己开始说的并不系统也不完整,一直耿耿于怀,特整理一下,以备后期之用和帮助后来者. package com.love.malin ...
- Invalid content was found starting with element 'mvc:exclude-mapping'.
问题?Invalid content was found starting with element 'mvc:exclude-mapping'. 这是springmvc中显著的错误,在配置拦截器的时 ...
- Identity Server 4 原理和实战(完结)_建立Identity Server 4项目,Client Credentials 授权实例
创建项目 dotnet new -i IdentityServer4.Templates 多出来的这些模板 adminUI用来测试,想要用再生产环境,需要交钱 结合core的 Identity来使用 ...
- 访问web-inf下jsp资源的几种方式
转自:http://blog.csdn.NET/eidolon8/article/details/7050114 方法一: 本来WEB-INF中的jsp就是无法通过地址栏访问的,所以安全. 如果说你要 ...
- bzoj 2007: [Noi2010]海拔【最小割+dijskstra】
上来就跑3e5的最大流--脑子抽了 很容易看出,每个地方的海拔都是0或1因为再高了没有意义,又,上去下来再上去没有意义,所以最后一定是从s连着一片0,剩下连着t一片1,然后有贡献的就是01交接的那些边 ...