java.util.concurrent

ConcurrentHashMap是一个支持并发检索和并发更新的线程安全的HashMap(但不允许空key或value)。

JDK8以CAS+synchronized来保证并发安全。

ConcurrentHashMap、HashMap和HashTable

效率:

  1. 当期望许多线程访问一个给定collection时,ConcurrentHashMap通常优于同步的HashMapConcurrentSkipListMap通常优于同步的TreeMap
  2. 当期望的读数和遍历远远大于列表的更新数时,CopyOnWriteArrayList优于同步的ArrayList

ConcurrentHashMap、HashMap和HashTable的区别:

  1. HashMap 是非线程安全的哈希表,常用于单线程程序中。
  2. Hashtable 是线程安全的哈希表,由于是通过内置锁 synchronized 来保证线程安全,在资源争用比较高的环境下,Hashtable 的效率比较低。
  3. ConcurrentHashMap 是一个支持并发操作的线程安全的HashMap,但是他不允许存储空key或value。使用CAS+synchronized来保证并发安全(在JDK 7之前是通过LockSegment(分段锁)实现并发安全),在并发访问时不需要阻塞线程,所以效率是比Hashtable 要高的。

结构

put(K, V)

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();
//计算hash值
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {//自旋
//f:索引节点; n:tab.length; i:新节点索引 (n - 1) & hash; fh:f.hash
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) {//索引i节点为空,直接插入
//cas插入节点,成功则跳出循环
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) {//check stable
//f.hash>=0,说明f是链表的头结点
if (fh >= 0) {
binCount = 1;//记录链表节点数,用于后面是否转换为红黑树做判断
for (Node<K,V> e = f;; ++binCount) {
K ek;
//key相同 修改
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) {
//如果链表中节点数binCount >= TREEIFY_THRESHOLD(默认是8),则把链表转化为红黑树结构
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
//更新新元素个数
addCount(1L, binCount);
return null;
}

get(Object key)

    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;
}

remove(Object key)

    public V remove(Object key) {
return replaceNode(key, null, null);
} final V replaceNode(Object key, V value, Object cv) {
int hash = spread(key.hashCode());
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0 ||
(f = tabAt(tab, i = (n - 1) & hash)) == null)
break;
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
boolean validated = false;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
validated = true;
for (Node<K,V> e = f, pred = null;;) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
V ev = e.val;
if (cv == null || cv == ev ||
(ev != null && cv.equals(ev))) {
oldVal = ev;
if (value != null)
e.val = value;
else if (pred != null)
pred.next = e.next;
else
setTabAt(tab, i, e.next);
}
break;
}
pred = e;
if ((e = e.next) == null)
break;
}
}
else if (f instanceof TreeBin) {
validated = true;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null &&
(p = r.findTreeNode(hash, key, null)) != null) {
V pv = p.val;
if (cv == null || cv == pv ||
(pv != null && cv.equals(pv))) {
oldVal = pv;
if (value != null)
p.val = value;
else if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
else if (f instanceof ReservationNode)
throw new IllegalStateException("Recursive update");
}
}
if (validated) {
if (oldVal != null) {
if (value == null)
addCount(-1L, -1);
return oldVal;
}
break;
}
}
}
return null;
}

JUC 一 ConcurrentHashMap的更多相关文章

  1. java并发编程(二十二)----(JUC集合)ConcurrentHashMap介绍

    这一节我们来看一下并发的Map,ConcurrentHashMap和ConcurrentSkipListMap.ConcurrentHashMap通常只被看做并发效率更高的Map,用来替换其他线程安全 ...

  2. 【转】阿里2015校招面试回忆(成功拿到offer)

    原文转自:http://blog.jobbole.com/78722/ 1. 引言 继上次<百度2015校园招聘面试题回忆(成功拿到offer)>文章过后,大家都希望除了题目之外,最好能给 ...

  3. 阿里2015回顾面试招收学历(获得成功offer)

    1. 引言 继上次"百度2015校园招聘面试题回顾录(成功拿到offer)"文章过后,大家都希望除了题目之外.最好能给出自己当时的回答情况,看看有没有什么回答技巧,这样更有參考价值 ...

  4. CopyOnWriteArrayList你都不知道,怎么拿offer?

    前言 只有光头才能变强 前一阵子写过一篇COW(Copy On Write)文章,结果阅读量很低啊...COW奶牛!Copy On Write机制了解一下 可能大家对这个技术比较陌生吧,但这项技术是挺 ...

  5. 阿里2015校招面试回忆录(成功拿到offer)

    1. 引言 继上次“百度2015校园招聘面试题回忆录(成功拿到offer)”文章过后,大家都希望除了题目之外,最好能给出自己当时的回答情况,看看有没有什么回答技巧,这样更有参考价值. 嗯,建议的很对, ...

  6. 学生党成功拿到阿里技术offer:面Java开发,却是C++考官,几个意思?

    摘要: 这是我为大家分享的如何拿到阿里技术offer系列文章中的第一篇,今天分享的文章的作者是一位在2015年阿里的校招中成功得到offer的美女学姐,从学姐的这篇文章中我们能学到很多在阿里面试的宝贵 ...

  7. 最强Java并发编程详解:知识点梳理,BAT面试题等

    本文原创更多内容可以参考: Java 全栈知识体系.如需转载请说明原处. 知识体系系统性梳理 Java 并发之基础 A. Java进阶 - Java 并发之基础:首先全局的了解并发的知识体系,同时了解 ...

  8. Java 集合系列之五:Map基本操作

    1. Java Map 1. Java Map 重要观点 Java Map接口是Java Collections Framework的成员.但是它不是Collection 将键映射到值的对象.一个映射 ...

  9. 剑指CopyOnWriteArrayList

    上期回顾 之前的一篇 剑指ConcurrentHashMap[基于JDK1.8] 给大家详细分析了一波JUC的ConcurrentHashMap,它在线程安全的基础上提供了更好的写并发能力.那么既然有 ...

随机推荐

  1. Ansible的roles标准化与Jenkins持续集成(三)

    Ansible的roles标准化与Jenkins持续集成(三) 链接:https://pan.baidu.com/s/1A3Iq3gGkGS27L_Gt37_I0g 提取码:ncy2 复制这段内容后打 ...

  2. Android 6.0 - 动态权限管理的解决方案(转)

    转自:http://www.cnblogs.com/dubo-/p/6018262.html Android 6.0 - 动态权限管理的解决方案   转载请标注 Android 6.0版本(Api 2 ...

  3. model字段对象和forms字段对象的区别和联系

    一.model字段对象 (一)_meta _meta是django.db.models.options.Options的实例,获取字段对象可通过模型类来进行获取,而_meta可提供如下功能: 获取模型 ...

  4. Django 自定义扩展命令

    import datetime import logger from django.conf import settings from django.db.models import Q from d ...

  5. SQL 按关键字排序

    SQL ORDER BY Keyword(按关键字排序) ORDER BY 关键字用于对结果集进行排序. SQL ORDER BY 关键字 ORDER BY 关键字用于按升序或降序对结果集进行排序. ...

  6. luoguP2709 小B的询问 [莫队]

    题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重 ...

  7. 模拟+双指针——cf1244E

    排一遍序然后用l,r指针进行移动,每次移动的是靠1,或靠n更近的那个指针 #include<bits/stdc++.h> using namespace std; typedef long ...

  8. 线段树双tag+差分数组——cf1208E

    写了一上午 /* 对于每个数组a[],先排序然后从大到小把a[i]放进线段树更新 设a[i]的位置是pos,那么其可更新的区间是[pos,w-(li-pos)] 线段树结点保存 tag=now表示当前 ...

  9. flutter 卡在Running Gradle task 'assembleDebug'...

    Android项目运行时出错 卡在Initializing gradle… 运行时会卡在Initializing gradle..., 此时因为Android项目会用到Gradle, 如果没有FQ,下 ...

  10. Django Shell 如何对数据库进行测试

    Django 如何对数据库进行测试 Python manager.py shell Import django django.setup() from my_app.models import * 开 ...