ConcurrentHashMap

  1. get:

    /***  根据键值key获取value,根据key.equals方法判断两个元素是否相同*  @param key 键*  @return 如果key存在则返回对应的value,否则返回null*/
    public V get(Object key) {Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;//将key的hashcode进一步散列,减少hash碰撞int h = spread(key.hashCode());//具体的元素信息是存在Node[]数组中,先判断key对应hash值映射到数组元素的位置释放有值//如果对应的数组位置没有值,直接返回null,否则继续判断if ((tab = table) != null && (n = tab.length) > 0 &&(e = tabAt(tab, (n - 1) & h)) != null) {//如果当前数组位置的元素的hash值和key值均相等,则直接返回if ((eh = e.hash) == h) {if ((ek = e.key) == key || (ek != null && key.equals(ek)))return e.val;}//如果hash值为负数,说明相同hash值的元素组成了红黑树,则直接在红黑数内部查找:TreeBin.findelse if (eh < 0)return (p = e.find(h, key)) != null ? p.val : null;//有相同hash值(发生了hash碰撞)的元素组成一个链表,依次在链表中查询目标元素while ((e = e.next) != null) {if (e.hash == h &&((ek = e.key) == key || (ek != null && key.equals(ek))))return e.val;}}return null;
    }
    

    可以看到get操作完全没有加锁,那么多线程操作的时候如何保证正确性呢?通过上面get操作的源码可以看到,每一个key,value会被封装成一个Node,继续看Node的源码.

  2. Node:

    static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;//value为volatile类型的,从而保证了多线程读写的可见性volatile V val;//发生hash碰撞时,记录下一个元素,类型也是volatile的,从而保证可见性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;}
    }
    

    这里ConcurrentHashMap是使用volatile来保证多线程操作的可见性的,从而避免了加锁逻辑。

  3. put:

    public V put(K key, V value) {//具体的实现逻辑在putValreturn putVal(key, value, false);
    }/*** 将key,value键值对放入到map中* @param key 键* @param value 值* @return 如果先前key的位置有值,则返回老的值,否则返回null*/
    final V putVal(K key, V value, boolean onlyIfAbsent) {//可以看到ConcurrentHashMap的key和value都不允许为nullif (key == null || value == null) throw new NullPointerException();//将key的hashcode进一步散列,减少hash碰撞int hash = spread(key.hashCode());int binCount = 0;for (Node<K,V>[] tab = table;;) {Node<K,V> f; int n, i, fh;//记录元素的Node数组为null时要先进行初始化if (tab == null || (n = tab.length) == 0)//初始化table,见下文分析tab = initTable();//如果当前位置为null,可以直接放入元素else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {//CAS写入元素,如果成功则返回;CAS失败说明有另外的线程在进行put操作,需要自旋等待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) {//防止其他线程更改tabb在i位置的值,如果发生更新则继续循环if (fh >= 0) {//hash值相同的节点为链表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;//putIfAbsent?if (!onlyIfAbsent)e.val = value;break;}Node<K,V> pred = e;//更新节点的next节点为新加入的节点if ((e = e.next) == null) {pred.next = new Node<K,V>(hash, key,value, null);break;}}}//如果hash值相同的节点为红黑树,则在红黑树内部执行节点新增或者更新逻辑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;}}}}//hash值相同的元素个数>0if (binCount != 0) {//节点超过8个,若<64则扩容链表,否则从链表转化为红黑树,提升查询效率if (binCount >= TREEIFY_THRESHOLD)treeifyBin(tab, i);if (oldVal != null)return oldVal;break;}}}//增加数组元素个数,并判断是否需要扩容,若需要则进行扩容操作addCount(1L, binCount);return null;
    }/*** 初始化Node数组*/
    private final Node<K,V>[] initTable() {Node<K,V>[] tab; int sc;//数组为空时,持续循环while ((tab = table) == null || tab.length == 0) {//sc == -1,说明有多个线程同时在执行初始化操作,此线程竞争失败,让出cpu执行权if ((sc = sizeCtl) < 0)Thread.yield(); //让出cpu控制权,自旋等待else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {//cas成功,进行初始化,并将sizectl的值设置为-1try {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;
    }
    
  4. 总结:可以看到ConcurrentHashMap的get操作完全没有加锁,put操作只有在hash碰撞的时候才会在冲突节点上加上Sychronized锁,整体的效率是非常高的。另外,需要注意的是:在执行put操作的时候,会进行扩容操作,而扩容时候比较耗时的,在实际应用过程中,如果需要大量数据的频繁写入,可以在初始化的时候指定一个较大的容量,避免频率扩容带来的开销。

Java并发编程之ConcurrentHashMap原理解析相关推荐

  1. Java 并发编程之 ConcurrentHashMap,ConcurrentSkipListMap

    Java并发容器Map类有两个,ConcurrentHashMap和ConcurrentSkipListMap,前者是无序的,后者有序. public class ConcurrentHashMapT ...

  2. Java并发编程之synchronized关键字解析

    前言 公司加班太狠了,都没啥时间充电,这周终于结束了.这次整理了Java并发编程里面的synchronized关键字,又称为隐式锁,与JUC包中的Lock显示锁相对应:这个关键字从Java诞生开始就有 ...

  3. Java并发编程之ConcurrentHashMap

    http://blog.csdn.net/hsuxu/article/details/9455563 转载于:https://www.cnblogs.com/hnucdj/p/4321144.html

  4. Java并发编程之CAS第三篇-CAS的缺点

    Java并发编程之CAS第三篇-CAS的缺点 通过前两篇的文章介绍,我们知道了CAS是什么以及查看源码了解CAS原理.那么在多线程并发环境中,的缺点是什么呢?这篇文章我们就来讨论讨论 本篇是<凯 ...

  5. java并发编程之4——Java锁分解锁分段技术

    转载自 java并发编程之4--Java锁分解锁分段技术 并发编程的所有问题,最后都转换成了,"有状态bean"的状态的同步与互斥修改问题.而最后提出的解决"有状态bea ...

  6. Java 并发编程之美:并发编程高级篇之一-chat

    借用 Java 并发编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了.相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作 ...

  7. Java 并发编程之美:并发编程高级篇之一

    借用 Java 并发编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了.相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作 ...

  8. Java并发编程之CyclicBarrier详解

    简介 栅栏类似于闭锁,它能阻塞一组线程直到某个事件的发生.栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行.闭锁用于等待事件,而栅栏用于等待其他线程. CyclicBarrier ...

  9. zbb20180929 thread java并发编程之Condition

    java并发编程之Condition 引言 在java中,对于任意一个java对象,它都拥有一组定义在java.lang.Object上监视器方法,包括wait(),wait(long timeout ...

最新文章

  1. maven创建的工程eclipse 项目--属性--为什么没有deployment assembly 按钮呢
  2. 驱动开发 环境搭建(VS2008+WDK+DDKWzard)
  3. 直博清华!陕西女学霸:从农村走出,3次斩获国奖,还被央视采访
  4. 我,1 倍开发者,有 20 条软件工程的经验法则
  5. [转]MFC中ON_COMMAND, ON_MESSAGE, ON_NOTIFY它们的区别
  6. RocketMQ之消费者并发消费源码解析
  7. 【译】Matplotlib:plotting
  8. 关于iBase4J使用的一点心得体会
  9. win10设置计算机关机时间,Win10怎么设置自动关机时间_Win10设置自动关机教程-192路由网...
  10. 生物信息学中用到的计算机知识,生物信息学期末复习知识点总结
  11. 使用opennlp进行词性标注
  12. 帝国CMS采集帝国模板程序通用
  13. css+js实现banner图片轮播
  14. Debian(Linux) 安装Windows通用字体(可解决TimesNewRoman等字体的报错)
  15. android 仿百度地图,仿百度地图街景实现
  16. 板线分离嵌入式RFID读卡模块NFC读写模块HX880系列的应用案例
  17. Entrust 将不再签发超过13个月有效期的SSL证书
  18. 为什么这位俄罗斯亿万富翁要为音乐节和音乐会创造一个虚拟现实世界?
  19. 【奇葩瑞萨-002】调教Renesas RX130独立看门狗
  20. 【渝粤题库】广东开放大学 跨文化交际学 形成性考核

热门文章

  1. python 太灵活_Python中的灵活参数
  2. ajax返回list前台遍历_微信返回列表页回到原浏览位置问题记录
  3. 疫情之下欧洲初创投资,德国竟成最大输家
  4. 东部985硕士毕业,北方二线省会军工所,还是上海互联网大厂?
  5. 智能行业热点速览(2019.7.15)
  6. Mac给Sublime Text 配置Python3开发环境
  7. 董明珠上榜中国杰出商界女性100
  8. 顶配12599元!三星Galaxy S22国行价格来了...
  9. 消息称ARM CEO已辞职 与660亿美元卖身NVIDIA失败无关
  10. 库克:iPhone 12更新换代用户数达到顶峰