一、概述

HashMap 是非线程安全的,在不考虑性能问题的时候,我们的解决方案有 Hashtable 或者Collections.synchronizedMap(hashMap),这两种方式基本都是对整个 hash 表结构做锁定操作的,这样在锁表的期间,别的线程就需要等待了,无疑性能不高。

二、数据结构

ConcurrentHashMap 数据结构为一个 Segment 数组,Segment 的数据结构为 HashEntry 的数组,而 HashEntry 存的是我们的键值对,可以构成链表。

ConcurrentHashMap 的结构中包含的 Segment 的数组,在默认的并发级别会创建包含 16 个 Segment 对象的数组。每个 Segment 又包含若干个散列表的桶,每个桶是由 HashEntry 链接起来的一个链表。如果 key 能够均匀散列,每个 Segment 大约守护整个散列表桶总数的 1/16。

三、segment

Segment 的类定义为static final class Segment<K,V> extends ReentrantLock implements Serializable。其继承于 ReentrantLock 类,从而使得 Segment 对象可以充当锁的角色。Segment 中包含HashEntry 的数组,其可以守护其包含的若干个桶(HashEntry的数组)。

四、并发put

在 ConcurrentHashMap 中,当执行 put 方法的时候,会需要加锁来完成。

需要注意的是:加锁操作是针对的 hash 值对应的某个 Segment,而不是整个 ConcurrentHashMap。因为 put 操作只是在这个 Segment 中完成,所以并不需要对整个 ConcurrentHashMap 加锁。所以,此时,其他的线程也可以对另外的 Segment 进行 put 操作,因为虽然该 Segment 被锁住了,但其他的 Segment 并没有加锁。同时,读线程并不会因为本线程的加锁而阻塞

ConcurrentHashMap 可以支持 16 个线程执行并发写操作(默认并发级别设置为 16),及任意数量线程的读操作。

五、读操作

无锁,对table进行定义为volatile,满足可见性

六、总结

散列表一般的应用场景是:除了少数插入操作和删除操作外,绝大多数都是读取操作,而且读操作在大多数时候都是成功的。

基于这个前提,ConcurrentHashMap 针对读操作做了大量的优化。通过 HashEntry 对象的不变性用 volatile 型变量协调线程间的内存可见性,使得 大多数时候,读操作不需要加锁就可以正确获得值。

ConcurrentHashMap 是一个并发散列映射表的实现,它允许完全并发的读取,并且支持给定数量的并发更新。相比于 HashTable 和用同步包装器包装的 HashMap(Collections.synchronizedMap(new HashMap())),ConcurrentHashMap 拥有更高的并发性。在 HashTable 和由同步包装器包装的 HashMap 中,使用一个全局的锁来同步不同线程间的并发访问。同一时间点,只能有一个线程持有锁,也就是说在同一时间点,只能有一个线程能访问容器。这虽然保证多线程间的安全并发访问,但同时也导致对容器的访问变成串行化的了。

ConcurrentHashMap 的高并发性主要来自于三个方面:

  • 分离锁实现多个线程间的更深层次的共享访问。
  • HashEntery 对象的不变性来降低执行操作的线程在遍历链表期间对加锁的需求。
  • 通过对同一个 Volatile 变量的写 / 读访问,协调不同线程间读 / 写操作的内存可见性。

使用分离锁,减小了请求 同一个锁的频率。

通过 HashEntery 对象的不变性及对同一个 Volatile 变量的读 / 写来协调内存可见性,使得 读操作大多数时候不需要加锁就能成功获取到需要的值。由于散列映射表在实际应用中大多数操作都是成功的 读操作,所以 2 和 3 既可以减少请求同一个锁的频率,也可以有效减少持有锁的时间。通过减小请求同一个锁的频率和尽量减少持有锁的时间 ,使得 ConcurrentHashMap 的并发性相对于 HashTable 和用同步包装器包装的 HashMap有了质的提高。

最新文章

  1. linux命令大全之ln命令详解(创建软链接和硬链接)
  2. js中数组遍历for与for in区别(强烈建议不要使用for in遍历数组)
  3. rpc优化
  4. printf 格式化最常用用法
  5. JDBC的使用——Statement
  6. C++惯用法:通过成员模板实现隐式转换(Coercion 强迫 by Member Template)
  7. RBAC用户角色权限设计方案
  8. ER模型
  9. CentOs Linux 常见命令
  10. hrbustoj 2130 一笔画(状态压缩)
  11. 小打卡PRD
  12. 启动SpringBoot的可执行jar 报错:target\spring-boot-hello-1.0-SNAPSHOT.jar中没有主清单属性
  13. webapi 重复提交问题
  14. nodejs进程线程优化性能
  15. Azkaban学习笔记(一)
  16. express session
  17. JS 时间转化为几分钟前 几小时前 几天前
  18. windows下vue项目启动步骤
  19. npp插件-NewFileBrowser:自定义模板
  20. BZOJ 1101 [POI2007]Zap | 第一道莫比乌斯反(繁)演(衍)

热门文章

  1. 根据数据库表自动生成实体类、xml和dao---mybatis
  2. MySQL-----一对一
  3. 初学数位DP
  4. source insight中的快捷键总结
  5. 分享大牛开发经验,浅谈java程序员职业规划
  6. Error connecting to database: No such file or directory
  7. [NOIP2007] 提高组 洛谷P1099 树网的核
  8. Python基础之 一 字典(dict)
  9. UVA 1025_A Spy in the Metro
  10. 洛谷 P1081 开车旅行(70)