一文读懂JAVA并发容器类ConcurrentHashMap
上文说了HashMap,其实HashMap是线程非安全的,JDK里面有个线程安全的就是HashTable,查看HashTable每个方法都增加了synchronized同步锁,也就说每次只能进入一个线程,这样影响效率。JDK源码也推荐使用ConcurrentHashMap。
推理ConcurrentHashMap的实现1.8
- ① JDK的描述
If a thread-safe implementation is not needed, it is recommended to use HashMap in place of code Hashtable. If a thread-safe highly-concurrent implementation is desired, then it is recommended to use ConcurrentHashMap in place of code Hashtable.
如果你不需要线程安全,那么使用HashMap,如果需要线程安全,那么使用ConcurrentHashMap。HashTable已经被淘汰了,不要在新的代码中再使用它。
- ② 描述
和 HashMap 非常类似,唯一的区别就是其中的核心数据如 value ,以及链表都是 volatile 修饰的,保证了获取时的可见性。原理上来说:根据 key 计算出 hashcode ,判断是否需要进行初始化,f 即为当前 key 定位出的 Node,如果为空表示当前位置可以写入数据,利用 CAS 尝试写入,多个线程操作只有一个成功,失败则自旋保证成功。如果当前位置的 hashcode == MOVED == -1,则需要进行扩容。如果都不满足,则利用 synchronized 锁写入数据。,如果数量大于 TREEIFY_THRESHOLD 则要转换为红黑树。。Mysql里面有表锁和行锁的概念。表锁就是HashTable,行锁就是ConcurrentHashMap 。
- ③ JDK1.8中的ConcurrentHashMap原理分析
1.7 已经解决了并发问题,并且能支持 N 个 Segment 这么多次数的并发,但依然存在 HashMap 在 1.7 版本中的问题。那么是什么问题呢?很明显那就是查询遍历链表效率太低。
因此 1.8 做了一些数据结构上的调整。,在 JAVA8 中它摒弃了 Segment(锁段)的概念,而是启用了一种全新的方式实现,利用 CAS 算法。底层依然由“数组”+链表+红黑树的方式思想,但是为了做到并发,又增加了很多辅助的类,例如 TreeBin、Traverser等对象内部类。如何让多线程之间,对象的状态对于各线程的“可视性”是顺序一致的:ConcurrentHashMap 使用了 happensbefore 规则来实现。happens-before规则(摘取自 JAVA 并发编程):
程序次序法则
线程中的每个动作A都 happens-before 于该线程中的每一个动作B,其中,在程序中,所有的动作B都能出现在A之后。
监视器锁法则
对一个监视器锁的解锁 happens-before 于每一个后续对同一监视器锁的加锁。
volatile 变量法则
对 volatile 域的写入操作 happens-before 于每一个后续对同一个域的读写操作。
线程启动法则
在一个线程里,对 Thread.start 的调用会 happens-before 于每个启动线程的动作。
线程终结法则
线程中的任何动作都 happens-before 于其他线程检测到这个线程已经终结、或者从 Thread.join 调用中成功返回,或 Thread.isAlive 返回 false。
中断法则
一个线程调用另一个线程的 interrupt happens-before 于被中断的线程发现中断。
终结法则
一个对象的构造函数的结束 happens-before 于这个对象 finalizer 的开始。
传递性
如果 A happens-before 于 B,且 B happens-before 于 C,则 A happens-before于C: 假设代码有两条语句,代码顺序是语句1先于语句2执行;那么只要语句之间不存在依赖关系,那么打乱它们的顺序对最终的结果没有影响的话,那么真正交给CPU去执行时,他们的执行顺序可以是先执行语句2然后语句1。
PS:不管是1.7的hashMap还是ConcurrentHashMap,源码的可读性变差。目前基本都是jdk1.8就没有说1.7的事情,毕竟事务都是在进化的。里面用到了很多数据结构,数据结构说难也不难,说容易也不容易,它本身就是人的思维的一种体现。好像说走路方式一样,怎么走都可以到指定的地方,但是方法不一样,数据结构就是通过更加科学方式来进行,归根到底还是看我的【数据结构与算法】专题。数据结构给你,通过算法来进行查找,如果是遍历的方式来查,可能相对于hash的形式要差一些。
- thinkcmf安装教程与目录结构详解 快速上手版
- finecms如何批量替换文章中的关键词?
- dedecms批量修改文章为待审核稿件怎么操作
- 群体智能算法-黏菌寻找食物最优路线行为模拟
- finecms设置伪静态后分享到微信不能访问怎么处理
- composer安装其实可以很简单 两行命令就解决了
- 章神的私房菜之数据预处理
- 如何调用finecms指定栏目的描述关键词
- finecms栏目文章页seo设置
- Logistic回归实战篇之预测病马死亡率(一)
- DedeCMS后台500错误一种原因是不支持PHP5.3、5.4及以上版本
- finecms指定从第几篇文章开始调用5条记录,并调用文章所在栏目
- finecms如何调用多个栏目的子栏目
- 从傅立叶变换到Gabor滤波器
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Jvm常量池、运行时常量池、字符串常量池理解
- 【Docker】命令使用大全
- Apache如何设置Enable Keep-Alive
- 【Docker】项目实战,部署自己的APP
- 用云开发快速构建最美AI毕业照小程序
- 13-2 vi-启动和退出
- Java中四种引用类型:强引用,软引用,弱引用,虚引用
- docker-compose搭建redis集群之哨兵模式
- docker-compose搭建redis集群之主从复制
- c/c++补完计划(七): 哨兵节点
- ASP.NET Core 奇技淫巧之接口代理转发
- SpringBoot 通过注解的方式整合 Mybatis + PageHelper 分页显示
- 将BX中的数以二进制形式在屏幕上显示出来。
- String及StringTable(二):java中的StringTable
- 设在起始地址为STRING的存储空间存放了一个字符串(该串已存放在内存中,无需输入,且串长不超过99),统计字符串中字符“A”的个数,并将结果显示在屏幕上。