ConcurrentHashMap笔记
概览:
内部存储的数据结构为:数组+链表+红黑树,图示:
重要的属性(内部类):
//存放元素的数组
transient volatile Node<K,V>[] table;
//数组中的Node节点
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;//Key计算出来的Hash值
final K key;//Key
volatile V val;//Value
volatile Node<K,V> next;//链表的下一个节点 Node(int hash, K key, V val, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.val = val;
this.next = next;
}
...//省略
}
//红黑树中的节点
static final class TreeNode<K,V> extends Node<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left; //左子节点
TreeNode<K,V> right; //右子节点
TreeNode<K,V> prev; //
boolean red;
...//省略
} //组合TreeNode
static final class TreeBin<K,V> extends Node<K,V> {
TreeNode<K,V> root;
volatile TreeNode<K,V> first;
} //内部类
方法分析
/** 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();//初始化数组大小,默认16
//数组指定位置元素为空,直接插入
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) {
//链表长度大于8,转为红黑树存储
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
/**
* Replaces all linked nodes in bin at given index unless table is
* too small, in which case resizes instead.
*/
private final void treeifyBin(Node<K,V>[] tab, int index) {
Node<K,V> b; int n, sc;
if (tab != null) {
if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
tryPresize(n << 1);//扩容数组
else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
synchronized (b) {
if (tabAt(tab, index) == b) {
TreeNode<K,V> hd = null, tl = null;
//遍历链表,构造TreeNode
for (Node<K,V> e = b; e != null; e = e.next) {
TreeNode<K,V> p =
new TreeNode<K,V>(e.hash, e.key, e.val,
null, null);
if ((p.prev = tl) == null)
hd = p;
else
tl.next = p;
tl = p;
}
//构建红黑树
setTabAt(tab, index, new TreeBin<K,V>(hd));
}
}
}
}
}
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
int h = spread(key.hashCode());
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
else if (eh < 0)//红黑树取值
return (p = e.find(h, key)) != null ? p.val : null;
//链表取值
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}
写在最后:
为什么使用红黑树?
红黑树的特性:
1、节点是红色或者黑色
2、根是黑色
3、所有叶子都是黑色
4、每个红色节点必须有2个黑色的子节点
5、从任一节点到其每个叶子的所有简单路径包含相同数目的黑色节点
根据特性5,从根的最长路径不可能>2倍的最短路径,所以这样的二叉树是平衡的;插入、删除、查询操作比较高效
拓展阅读
1、红黑树介绍 2、深入分析ConcurrentHashMap1.8的扩容实现
ConcurrentHashMap笔记的更多相关文章
- Java基础知识强化之集合框架笔记76:ConcurrentHashMap之 ConcurrentHashMap简介
1. ConcurrentHashMap简介: ConcurrentHashMap是一个线程安全的Hash Table,它的主要功能是提供了一组和Hashtable功能相同但是线程安全的方法.Conc ...
- java并发编程笔记(十)——HashMap与ConcurrentHashMap
java并发编程笔记(十)--HashMap与ConcurrentHashMap HashMap参数 有两个参数影响他的性能 初始容量(默认为16) 加载因子(默认是0.75) HashMap寻址方式 ...
- Java并发编程笔记之ConcurrentHashMap原理探究
在多线程环境下,使用HashMap进行put操作时存在丢失数据的情况,为了避免这种bug的隐患,强烈建议使用ConcurrentHashMap代替HashMap. HashTable是一个线程安全的类 ...
- java Concurrent包学习笔记(七):ConcurrentHashMap
(注意:以下讲解的ConcurrentHashMap是jdk 1.8的) 一.ConcurrentHashMap的数据结构 ConcurrentHashMap在1.8中的实现,相比于1.7的版本基本上 ...
- Java基础知识强化之集合框架笔记78:ConcurrentHashMap之 ConcurrentHashMap、Hashtable、HashMap、TreeMap区别
1. Hashtable: (1)是一个包含单向链的二维数组,table数组中是Entry<K,V>存储,entry对象: (2)放入的value不能为空: (3)线程安全的,所有方法均用 ...
- Java基础知识强化之集合框架笔记77:ConcurrentHashMap之 ConcurrentHashMap的基本操作
1. ConcurrentHashMap的初始化: 下面我们来结合源代码来具体分析一下ConcurrentHashMap的实现,先看下初始化方法: public ConcurrentHashMap(i ...
- JDK源码阅读(7):ConcurrentHashMap类阅读笔记
ConcurrentHashMap public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implement ...
- Java性能调优笔记
Java性能调优笔记 调优步骤:衡量系统现状.设定调优目标.寻找性能瓶颈.性能调优.衡量是否到达目标(如果未到达目标,需重新寻找性能瓶颈).性能调优结束. 寻找性能瓶颈 性能瓶颈的表象:资源消耗过多. ...
- effective java 第2章-创建和销毁对象 读书笔记
背景 去年就把这本javaer必读书--effective java中文版第二版 读完了,第一遍感觉比较肤浅,今年打算开始第二遍,顺便做一下笔记,后续会持续更新. 1.考虑用静态工厂方法替代构造器 优 ...
随机推荐
- ubutu14.04无法使用sudo,也无法切换到root用户去解决问题怎么办?
一不小心,修改了/etc/sudoers文件. 惨了. 无法使用sudo了,啥都干不成了. 最最关键的是,也无法用root登录. 本想着要重装系统了. 后来发现了神奇的ubuntu安全模式. 1.重启 ...
- oracle 存储过程调用 执行
oracle 存储过程调用 博客分类: 数据库相关 oracle存储过程 2011年02月11日 星期五 14:47 SQL中调用存储过程语句: call procedure_name(); 调用 ...
- 就是这个foxmail有时出现记事同步不了
昨天和妈妈商量还是要打算再次买一块正版电池,虽然她现在有点不情愿,但是过一个月再说,然后就是他的号码超出流量6M,由于是2G的流量,按照每M是一元,所以就是扣了6元,然后他的套餐里面显示还有94M也不 ...
- E20180120-hm
derive vt. 得到,导出; 源于,来自; (从…中) 提取; hierarchy n. [计] 分层,层次; 等级制度; 统治集团; 天使的级别或等级; inheritance n. 继承 ...
- 堆和栈的区别【以java为例潜入分析】
Java的堆是一个运行时数据区,类的对象从中分配空间,这些对象通过new等指令建立. 堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动 ...
- Windows环境下使用Netsh命令快速切换IP配置
不同的内网环境需要使用不同的IP配置,频繁切换令人发狂,因此搜索了快速切换IP配置的方法. Netsh interface IP Set address "以太网" Static ...
- Android 性能优化(6)网络优化( 2) Analyzing Network Traffic Data:分析网络数据
Analyzing Network Traffic Data 1.This lesson teaches you to Analyze App Network Traffic Analyze Netw ...
- DateFormat 多线程问题
在写实时应用解析日志的时候,有如下代码: public class CalPvLogParse { private static SimpleDateFormat logTimeFormat = ne ...
- iOS浏览器不能打开手机QQ客服与指定用户聊天界面
这个问题是我在公司需求的时候遇到的,QQ推广工具网站获取的链接在苹果自带浏览器没法打开到聊天界面,是因为safair在打开到app store的时候把参数给丢了,app store再打开到QQ的时候就 ...
- Java易忘知识点统计
缺少 内容 替代措施 幂运算 借助Math类的pow方法 注意 内容 备注 const Java保留关键字,未使用 其他 强制类型转换时,若要舍入得到最接近的整数,可以使用Math.round方法 J ...