ConcurrentHashMap的put方法
使用JDK8
源码:
public V put(K key, V value) {
return putVal(key, value, false);
}
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
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();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
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;
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) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
分析:
put
从put方法进入,然后进入putVal方法,可以看到onlyIfAbsent的值为false。
那么我们put和putIfAbsent对比:
public V putIfAbsent(K key, V value) {
return putVal(key, value, true);
}
onlyIfAbsent为true,当该位置为没有值,则加入。如果该位置有值,则保持原来的值,不进行覆盖。
如果大家还是不清楚那就看看网上关于putIfAbsent方法的使用讲解。
putVal
spread方法计算hash
进入table数组(ConcurrentHashMap结构是Node数组+链表+红黑树)
如果table数组为null,则先
initTable方法来初始化数组private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
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;
}
sizeCtl小于零说明有其他线程也在初始化数组,则让当前线程yield,让出cpu时间片。那如果我们自己初始化数组,则需要CAS加自旋。
如果该数组位置上没有元素,那么则直接添加
如果f.hash等于-1,说明需要扩容了
最后我们对链表头结点,或者对红黑树头结点加锁
如果是链表,遍历之,如果遇到key的值(注意不是key的hash值)和链表中的key的值有相等的,则覆盖value。如果遍历到最后都没遇到key值一样的,那就放到链表结尾吧。
如果是树,那就用
putTreeVal方法了
ConcurrentHashMap的put方法的更多相关文章
- ConcurrentHashMap的size方法是线程安全的吗?
前言 之前在面试的过程中有被问到,ConcurrentHashMap的size方法是线程安全的吗? 这个问题,确实没有答好.这次来根据源码来了解一下,具体是怎么一个实现过程. ConcurrentHa ...
- concurrentHashMap的put方法详解
本文主要介绍ConcurrentHashMap的put操作如果有错误的地方欢迎大家指出. 1.ConcurrentHashMap的put操作 ConcurrentHashMap的put操作主要有3种方 ...
- ConcurrentHashMap的size()方法(1.7和1.8)
在1.7和1.8版本中,计算size()方法有写不同.先介绍1.7版本的实现. 1.7版本 在1.7版本中,有一个重要的类Segment,利用它来实现分段锁 static final class Se ...
- 并发容器ConcurrentHashMap#put方法解析
jdk1.7.0_79 HashMap可以说是每个Java程序员用的最多的数据结构之一了,无处不见它的身影.关于HashMap,通常也能说出它不是线程安全的.这篇文章要提到的是在多线程并发环境下的Ha ...
- 8.并发容器ConcurrentHashMap#put方法解析
jdk1.7.0_79 HashMap可以说是每个Java程序员用的最多的数据结构之一了,无处不见它的身影.关于HashMap,通常也能说出它不是线程安全的.这篇文章要提到的是在多线程并发环境下的Ha ...
- ConcurrentHashMap内存泄漏问题
问题背景 上周,同事写了一段ConcurrentHashMap的测试代码,说往map里放了32个元素就内存溢出了,我大致看了一下他的代码及运行的jvm参数,觉得很奇怪,于是就自己捣鼓了一下.首先上一段 ...
- Java多线程系列--“JUC集合”04之 ConcurrentHashMap
概要 本章是JUC系列的ConcurrentHashMap篇.内容包括:ConcurrentHashMap介绍ConcurrentHashMap原理和数据结构ConcurrentHashMap函数列表 ...
- Java并发编程:并发容器之ConcurrentHashMap(转载)
Java并发编程:并发容器之ConcurrentHashMap(转载) 下面这部分内容转载自: http://www.haogongju.net/art/2350374 JDK5中添加了新的concu ...
- JDK1.7 ConcurrentHashMap 源码浅析
概述 ConcurrentHashMap是HashMap的线程安全版本,使用了分段加锁的方案,在高并发时有比较好的性能. 本文分析JDK1.7中ConcurrentHashMap的实现. 正文 Con ...
- Java ConcurrentHashMap
通过分析Hashtable就知道,synchronized是针对整张Hash表的,即每次锁住整张表让线程独占, ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术. ...
随机推荐
- GPTs prompts灵感库:创意无限,专业级创作指南,打造吸睛之作的秘诀
GPTs prompts灵感库:创意无限,专业级创作指南,打造吸睛之作的秘诀 优质prompt展示 1.1 极简翻译 中英文转换 你是一个极简翻译工具,请在对话中遵循以下规则: - Prohibit ...
- 辣鸡 mac 下 pycharm 中代码拖拽的问题
Editor –> General Enable Drag'n'Drop functionality in Editor
- static_cast, dynamic_cast与reinterpret_cast的区别
在C++中,static_cast, dynamic_cast和reinterpret_cast都可用于类型转换,它们在具体使用时有什么区别?此外,更为重要的是,为什么不推荐使用强制类型转换? 1. ...
- 洛谷P2670 扫雷游戏 关于区域搜索标记的坐标增量法
最简单的思路就是扫描一边所有节点,对每个非地雷节点,去检查一下他八个方向的元素是否有'*',有的话就加一 但是逐个写出有点麻烦,我们不妨定义两个增量数组来存储每一次的相对位移,对每次检查只需要遍历这个 ...
- 树莓派安装Ubuntu系统
树莓派版本: Raspberry Pi 4B 操作系统 : Ubuntu Server 20.04_x64 树莓派官网: https://www.raspberrypi.org/ ubuntu官网: ...
- Nginx实战-公网LB限流配置等
前提: Nginx要实现根据ip地址进行限流与不限流的区分需要通过源码包安装GeoIP模块 找到与yum安装版本相同的源码包,通过configure进行安装 ./configure --prefix= ...
- GaussDB(for MySQL) Serverless全面商用:无感弹性,极致性价比
本文分享自华为云社区<GaussDB(for MySQL) Serverless全面商用:无感弹性,极致性价比>,作者: GaussDB 数据库. 技术背景 对于现代企业级IT系统,数据库 ...
- Git 分支与合并
1. Git 对象 Git 的核心部分是一个简单的键值对数据库.可以向 Git 仓库中插入任意类型的内容,它会返回一个唯一的键,通过该键可以在任意时刻再次取回该内容. 所有内容均以树对象和数据对象的 ...
- Rancher 2.x 安装
Rancher 是一个容器管理平台.Rancher 简化了使用 Kubernetes 的流程. 下面记录一下手动安装Rancher的步骤 1. 部署 Rancher Server 执行以下命令即可( ...
- 【OpenGL ES】绘制圆形
1 前言 [OpenGL ES]绘制三角形 中介绍了绘制三角形的方法,[OpenGL ES]绘制正方形中介绍了绘制正方形的方法,本文将介绍绘制圆形的方法. OpenGL 以点.线段.三角形为图 ...