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. dubbo-源码阅读之Filter实现原理

    最近完成一个需求,使用阿里Arms需要在log里面加上traceId,但是发现dubbo异常 被ExceptionFilter捕获 并打印  打印不出traceI,然后百度搜索如何重写Filter 参 ...

  2. java是否是“美丽的”语言

    java 学习  (jdk扫描   配置读取   服务器选项  )

  3. centos 单用户登陆模式操作

    在centos中因为安装java而配置 jdk环境变量的原因,对/etc/profile文件进行了编辑 错误的环境变量配置导致在第一次修改profile文件并保存后,执行source /etc/pro ...

  4. spring.xml及注解

    spring.xml配置文件中配置注解: 开启注解(及自动扫描包中bean): 1:<context:component-scan base-package="com.bzu" ...

  5. C# 16进制转字符串,字符串转16进制

    { //========================================================== //16进制转字符串 public static byte[] HexTo ...

  6. session控制登入权限

    <?php session_start(); if(empty($_SESSION["uid"]))//判断SESSION是不是为空 { header("locat ...

  7. NOIp2018集训test-10-20 (bike day6)

    B 君的第一题 lanzhou $x^{\frac{p-1}{2}}\equiv 1(mod\ p)$ $x\equiv x*x^{\frac{p-1}{2}} (mod\ p)$ $x\equiv ...

  8. 使用gulp管理sass文件

    前提是npm和ruby已经安装好 1. 新建文件夹myproject,cd进入文件夹 再npm init 初始化 2.npm install gulp --save-dev 为项目添加gulp,并将g ...

  9. VirtualBox的源码学习

    VMM重要的组件 TRPM (Trap Manager) PGM (Page Manager) REM (Recompiled Execution Manager) EM (Execution Man ...

  10. vue组件通信之非父子组件通信

    什么顺序不顺序的.. 先来说说非父子组件通信. 首先,我们先来了解下vue中的 1.$emit  触发当前实例上的事件,附加参数都会传给监听器回调. 2.$on  监听当前实例上的自定义事件.事件可以 ...