java源码-ConcurrentHashMap分析-1
ConcurrentHashMap源码分析 版本jdk8 摈弃了jdk7之前的segement段锁:
首先分析一下put方法,大致的流程就是首先对key取hash函数 判断是否first节点是否存在 不存在则 cas更新,存在 判断是否是forward节点,如果是则帮助扩容,否则锁住first节点 然后循环遍历链表判断事够key.equals()跟hash是否相等,
相等则直接替换旧值,如果遍历到链表next==null,则直接新建一个node,然后next指向他,最后在调用addCount()方法并发统计跟扩容
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
// 可见concurrentHashMap不支持key 跟 value为空
if (key == null || value == null) throw new NullPointerException();
// 通过hash函数得到hash值
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
// 如果表为空 则开始初始化表
if (tab == null || (n = tab.length) == 0)
tab = initTable();
// hash & (n-1) 得到数组索引值, 为空
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
// cas更新头节点 这边可能多线程更新头节点,其他线程更新失败后则开始继续循环
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
// 如果节点是forwoard节点 则开始帮助扩容
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
// 锁住头节点
synchronized (f) {
// 再次判断i节点是否等于f 有可能被其他线程修改
if (tabAt(tab, i) == f) {
// -1 hash for forwarding nodes
// -2 hash for roots of trees
// -3 hash for transient reservations
if (fh >= 0) {
binCount = 1;
// 循环遍历链表
for (Node<K,V> e = f;; ++binCount) {
K ek;
// 如果hash相同 && key.equals(ek)) onlyIfAbsent=false 则直接替换旧值 跳出循环
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e ;
// 如果 fisrt节点没有next节点 直接新建节点设置next指向新节点 如果不为空则 将e指向next节点 继续循环
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
// 如果是 红黑树则走红黑树插入逻辑
else if (f instanceof 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) {
// 如果链表长度大于8则开始转换红黑树
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
// 并发统计count 扩容操作
// 初始化表
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
// sizeCtl 小于0 代表在初始化或扩容
if ((sc = sizeCtl) < 0)
//此时放弃cpu
Thread.yield(); // lost initialization race; just spin
// 并发修改sizeCtl为-1
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
// 初始化node数组 最后赋予sizeCtl=sc
try {
if ((tab = table) == null || tab.length == 0) {
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
java源码-ConcurrentHashMap分析-1的更多相关文章
- Java 源码如何分析?
如何阅读源码?万事开头难,源码从哪里开始看?我也是刚对源码的阅读研究不深,但是可以谈谈自己的源码阅读感受. 刚开始吧,只是对某些代码的实现原理感到好奇,好奇是怎么实现这种功能,实现这种效果的,对其背后 ...
- [Java] Hashtable 源码简要分析
Hashtable /HashMap / LinkedHashMap 概述 * Hashtable比较早,是线程安全的哈希映射表.内部采用Entry[]数组,每个Entry均可作为链表的头,用来解决冲 ...
- JDK(十)JDK1.7&1.8源码对比分析【集合】ConcurrentHashMap
前言 在JDK1.7&1.8源码对比分析[集合]HashMap中我们对比分析了JDK1.7和1.8版本的HashMap源码,趁热打铁,这篇文章就来看看JDK1.7和1.8版本的Concurre ...
- Java源码分析 | CharSequence
本文基于 OracleJDK 11, HotSpot 虚拟机. CharSequence 定义 CharSequence 是 java.lang 包下的一个接口,是 char 值的可读序列, 即其本身 ...
- [Java] LinkedHashMap 源码简要分析
特点 * 各个元素不仅仅按照HashMap的结构存储,而且每个元素包含了before/after指针,通过一个头元素header,形成一个双向循环链表.使用循环链表,保存了元素插入的顺序. * 可设置 ...
- [Java] HashMap 源码简要分析
特性 * 允许null作为key/value. * 不保证按照插入的顺序输出.使用hash构造的映射一般来讲是无序的. * 非线程安全. * 内部原理与Hashtable类似. 源码简要分析 pu ...
- Java 源码刨析 - HashMap 底层实现原理是什么?JDK8 做了哪些优化?
[基本结构] 在 JDK 1.7 中 HashMap 是以数组加链表的形式组成的: JDK 1.8 之后新增了红黑树的组成结构,当链表大于 8 并且容量大于 64 时,链表结构会转换成红黑树结构,它的 ...
- HashMap源码实现分析
HashMap源码实现分析 一.前言 HashMap 顾名思义,就是用hash表的原理实现的Map接口容器对象,那什么又是hash表呢. 我们对数组都很熟悉,数组是一个占用连续内存的数据结构,学过C的 ...
- MapReduce的ReduceTask任务的运行源码级分析
MapReduce的MapTask任务的运行源码级分析 这篇文章好不容易恢复了...谢天谢地...这篇文章讲了MapTask的执行流程.咱们这一节讲解ReduceTask的执行流程.ReduceTas ...
随机推荐
- sscanf功能详解(转)
在处理字符串的程序当中,经常会分析字符串,从一大长串的字符串截取我们需要的数据,这如果通过自己手写函数来分析,虽然可以,但当你知道sscanf的功能,那就自己写函数分析就显得多此一举. 这些函数的使用 ...
- maven将本地jar包引入本地maven仓库命令
一.maven安装命令.cmd窗口,idea中均可 mvn install:install-file -Dfile=F:\coding2pay\pay\lib/wxpay-sdk-.jar -Dgro ...
- 检验注解@Validated 和@valid说明
- Java 使用jsp和servlet实现验证码功能
验证码主要是为了防止我们的网站被有些人和黑客恶意攻击,比如我们网站的注册页面,如果我们在用户注册的时候不加上一个验证码框的话,别人就可以写一个脚本对你的网站进行恶意的注册,比如每分钟对你的网站进行n次 ...
- pro git 读书笔记 3 Git 分支
分支 新建分支:git branch 分支名 切换到该分支:git checkout 分支名 补充:以上两条语句等价于一句:git checkout -b 分支名 转换分支的时候最好保持一个清洁的工作 ...
- 【51nod 1245】Binomial Coefficients Revenge
题目大意 C(M,N) = M! / N! / (M - N)! (组合数).给出M和质数p,求C(M,0), C(M,1)......C(M,M)这M + 1个数中,有多少数不是p的倍数,有多少是p ...
- mysql随机取出n条数据
SELECT * FROM tableName ORDER BY RAND() LIMIT n 数据量小的话还可以, 数据量大起来了, 就影响性能了. $rubbish = (new ...
- java 项目 文件关系 扫描 注释注入(3)
@RequestParam和@PathVariable用法小结 https://www.cnblogs.com/helloworld-hyyx/p/5295514.html(copy) @Reque ...
- LA 4223 最短路 路径选择要求提高一点
F - Trucking Time Limit:3000MS Memory Limit:0KB 64bit IO Format:%lld & %llu Submit Statu ...
- 实现类数组转化成数组(DOM 操作获得的返回元素值是一个类数组)
目标 实现类数组转化成数组 实例 链接地址 使用方法 const foo = document.querySelectorAll('.result') //链接地址输入控制台输入这行代码 const ...