【JDK源码系列】ConcurrentHashMap

ConcurrentHashMap的关键实现:分段锁,简单来讲就是将hash表分成一段段子表,分别加锁。本质上ConcurrentHashMap和hashMap没什么区别,元素都是Entry,一个节点加链表。发生冲突的时候采用链表法存储元素,所以源码里会有大量的通过key的hash值找到槽后进行遍历查询的操作。

下面是子表的源码实现:

自定义了一个segment类,继承了可重入锁。可以看一下整体的结构:

其中比较关键的应该是put,rehash,scanandlock,remove,replace等方法。

我们先来看segment的put方法:

对map中的某个segment做put操作,加入我们来实现一般会考虑到几点:

  1. 需要加锁,因为要考虑到key相同的情况,如果不加锁,两个线程同时更新同一个key的value,会出错。
  2. 需要考虑锁已经被占的情况。
  3. 需要更新segment的元素个数。
  4. 需要考虑segment扩容的问题。

带着这些考虑,我们再来看源码可能就会更有针对性一些。

第一步果然是加锁,这里直接用了trylock,因为segment继承了可重入锁,其实这是个很垃圾的设计,按理来说应该优先使用组合而不是继承。我们可以看到,尝试获取锁失败后,调用了scanAndLockForPut方法,这个方法会不断尝试获取锁,同时去匹配entry的key值,直到成功,重新回到put方法。

Put方法里考虑到了扩容的问题:

假如元素个数大于阈值,会调用rehash方法。Rehash首先进行了数组的扩容:

同时对老的元素进行迁移的时候进行了rehash操作: idx = e.hash&sizeMask

最后put方法会更新元素总数:

下面我们来看一下remove方法:

同样的我们首先来自己思考一下哪些点是需要考虑到的:

  1. 需要枷锁,同时考虑锁被抢占,同插入
  2. 更新元素个数

首先是尝试获取锁,如果锁被占了,那么等待获取。

查找的时候先获取Entry,然后遍历链表:

找到元素后进行普通的链表移除元素操作,并更新元素总数。

这里有点奇怪的地方,modeCount为什么加一了?这点后面解答。

Replace和remove操作几乎一致,这里不再赘述。

时间: 2024-10-12 01:21:58

【JDK源码系列】ConcurrentHashMap的相关文章

JDK 源码解析 —— ConcurrentHashMap

零. 概述 ConcurrentHashMap 是将锁的范围细化来实现高效并发的. 基本策略是将数据结构分为一个一个 Segment(每一个都是一个并发可读的 hash table, 即分段锁)作为一个并发单元. 为了减少开销, 除了一处 Segment 是在构造器初始化的, 其他都延迟初始化(详见 ensureSegment). 并使用 volatile 关键字来保证 Segment 延迟初始化的可见性问题. HashMap 不是线程安全的, 故多线程情况下会出现 infinit loop.

JDK源码系列(一) ------ 深入理解SPI机制

什么是SPI机制 最近我建了另一个文章分类,用于扩展JDK中一些重要但不常用的功能. SPI,全名Service Provider Interface,是一种服务发现机制.它可以看成是一种针对接口实现类的解耦方案.我们只需要采用配置文件方式配置好接口的实现类,就可以利用SPI机制去加载到它们了,当我们需要修改实现类时,改改配置文件就可以了,而不需要去改代码. 当然,有的同学可能会问,spring也可以做接口实现类的解耦,是不是SPI就没用了呢?虽然两者都可以达到相同的目的,但是不一定所有应用都可

JDK源码系列(3):CyclicBarrier

Semaphore 1.介绍 Semaphore是一个计数信号量,可以控同时访问的线程个数,它的本质是一个"共享锁". 信号量维护了一个信号量许可集.线程可以通过调用acquire()来获取信号量的许可:当信号量中有可用的许可时,线程能获取该许可:否则线程必须等待,直到有可用的许可为止. 线程可以通过release()来释放它所持有的信号量许可. 结构示意图 1.1.核心接口/方法定义 public void acquire() throws InterruptedException

JDK源码学习系列07----Stack

                                                               JDK源码学习系列07----Stack 1.Stack源码非常简单 package java.util; public class Stack<E> extends Vector<E> { // 版本ID.这个用于版本升级控制,这里不须理会! private static final long serialVersionUID = 122446316454

JDK源码学习系列08----HashMap

                                                          JDK源码学习系列08----HashMap 1.HashMap简介 HashMap 是一个散列表,它存储的内容是键值对(key-value)映射. HashMap 继承于AbstractMap,实现了Map.Cloneable.java.io.Serializable接口. HashMap 的实现不是同步的,这意味着它不是线程安全的.它的key.value都可以为null.此外,

JDK源码学习系列06----Vector

                                            JDK源码学习系列06----Vector 1.Vector简介 Vector的内部是数组实现的,它和ArrayList非常相似,最大的不同就是 Vector 是线程安全(同步)的. public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.S

JDK源码学习系列05----LinkedList

                                         JDK源码学习系列05----LinkedList 1.LinkedList简介 LinkedList是基于双向链表实现的,它也可以被当作堆栈.队列或双端队列进行操作. public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, jav

JDK源码那些事儿之并发ConcurrentHashMap上篇

前面前已经说明了HashMap以及红黑树的一些基本知识,对JDK8的HashMap也有了一定的了解,本篇就开始看看并发包下的ConcurrentHashMap,说实话,还是比较复杂的,笔者在这里也不会过多深入,源码层次上了解一些主要流程即可,清楚多线程环境下整个Map的运作过程就算是很大进步了,更细的底层部分需要时间和精力来研究,暂不深入 前言 jdk版本:1.8 JDK7中,ConcurrentHashMap把内部细分成了若干个小的HashMap,称之为段(Segment),默认被分为16个段

AOP执行增强-Spring 源码系列(5)

AOP增强实现-Spring 源码系列(5) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProcessor-Spring 源码(3) 事件机制-Spring 源码(4) AOP执行增强-Spring 源码系列(5) AOP的核心就是个动态代理,Spring进行了大量抽象和封装形成一个方便上层使用的基础模块. 而动态代理的两种实现都在上一篇中提供了代码 直接ProxyFactoryBean入手来