参考:https://www.cnblogs.com/liuyun1995/p/8631264.html

HashMap不是线程安全的,其所有的方法都未同步,虽然可以使用Collections的synchronizedMap方法使其线程安全,但是针对的只是当前的map对象。对此,

JDK提供了线程安全的Hashtable,其所有的方法都是同步的,即使用Synchronized关键字进行加锁,但是也导致了同一时刻只能有一个线程操作哈希表,影响

性能,所以又提供了效率较高的线程安全的哈希表ConcurrentHashMap,其内部使用分段锁将锁进行细粒度化,从而使多个线程能够同时操作哈希表,大大提

高了性能。下面是其内部结构示意图:

Like {@link Hashtable} but unlike {@link HashMap}, this class
 does <em>not</em> allow {@code null} to be used as a key or value

和Hashtable一样,不允许null键和null值

Concurrent1.7和1.8的区别:

1.7中采用Segment+HashEntry的方式进行实现,并发控制使用ReentrantLock来实现。

  每一个Segment元素存储的是HashEntry数组+链表,和HashMap的数据结构相同

1.8中放弃了Segment臃肿的设计,取而代之的是采用Node数组+链表+红黑树的数据结构来实现,当链表的长度大于8时会转化为红黑树。

  并发控制使用 Synchronized+CAS来保证

  JDK1.8版本的ConcurrentHashMap的数据结构已经接近HashMap,相对而言,ConcurrentHashMap只是增加了同步的操作来控制并发,从JDK1.7版本的ReentrantLock+Segment+HashEntry,到JDK1.8版本中synchronized+CAS+HashEntry+红黑树,相对而言,总结如下思考

JDK1.8的实现降低锁的粒度,JDK1.7版本锁的粒度是基于Segment的,包含多个HashEntry,而JDK1.8锁的粒度就是HashEntry(首节点)
JDK1.8版本的数据结构变得更加简单,使得操作也更加清晰流畅,因为已经使用synchronized来进行同步,所以不需要分段锁的概念,也就不需要Segment这种数据结构了,由于粒度的降低,实现的复杂度也增加了
JDK1.8使用红黑树来优化链表,基于长度很长的链表的遍历是一个很漫长的过程,而红黑树的遍历效率是很快的(logn),代替一定阈值的链表,这样形成一个最佳拍档

JDK1.8为什么使用内置锁synchronized来代替重入锁ReentrantLock:
  1、因为粒度降低了,在相对而言的低粒度加锁方式,synchronized并不比ReentrantLock差,在粗粒度加锁中ReentrantLock可能通过Condition来控制各个低粒度的边界,

    更加的灵活,而在低粒度中,Condition的优势就没有了
  2、JVM的开发团队从来都没有放弃synchronized,而且基于JVM的synchronized优化空间更大,使用内嵌的关键字比使用API更加自然
  3、在大量的数据操作下,对于JVM的内存压力,基于API的ReentrantLock会开销更多的内存,虽然不是瓶颈,但是也是一个选择依据

  原文:https://blog.csdn.net/qq296398300/article/details/79074239

以下源码基于Java 1.7版本

public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
implements ConcurrentMap<K, V>, Serializable {
private static final long serialVersionUID = 7249069246763182397L;

其成员变量:

  /**
* The maximum number of segments to allow; used to bound
* constructor arguments. Must be power of two less than 1 << 24.
   * 分段锁的最大数量
*/
static final int MAX_SEGMENTS = 1 << 16; /**
* Number of unsynchronized retries in size and containsValue
* methods before resorting to locking. This is used to avoid
* unbounded retries if tables undergo continuous modification
* which would make it impossible to obtain an accurate result.
   * 分段锁的最小数量
*/
static final int RETRIES_BEFORE_LOCK = 2;
/**
* Mask value for indexing into segments. The upper bits of a
* key's hash code are used to choose the segment.
* 分段锁的掩码值
*/
final int segmentMask; /**
* Shift value for indexing within segments.
* 分段锁的移位值
*/
final int segmentShift; /**
* The segments, each of which is a specialized hash table.
* 分段锁数组
*/
final Segment<K,V>[] segments;

Segment是ConcurrentHashpMap的一个静态内部类:

 static final class Segment<K,V> extends ReentrantLock implements Serializable {
static final int MAX_SCAN_RETRIES =
Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
transient volatile HashEntry<K,V>[] table;
transient int count;
transient int modCount; transient int threshold;
final float loadFactor;

Segment继承了ReentrantLock ,所以其本质上是一个锁。

分段锁即是由Segment来实现的,它继承于ReentrantLock,用来管理它辖区内的各个HashEntry。ConcurrentHashMap被Segment分成了很多小区,Segment就相当于小区保安,

HashEntry列表相当于小区业主,小区保安通过加锁的方式,保证每个Segment内都不发生冲突。

put和remove方法,对于Segment内部元素和计数器的更新,全部处于锁的保护下,如Segment.put()方法的第一行:

HashEntry<K,V> node = tryLock() ? null : scanAndLockForPut(key, hash, value);
												

ConcurrentHashMap1.7源码分析的更多相关文章

  1. ConcurrentHashMap1.8源码分析

    文章简介 想必大家对HashMap数据结构并不陌生,JDK1.7采用的是数组+链表的方式,JDK1.8采用的是数组+链表+红黑树的方式.虽然JDK1.8对于HashMap有了很大的改进,提高了存取效率 ...

  2. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  3. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

  4. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  5. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  6. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  7. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

  8. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  9. ABP源码分析二:ABP中配置的注册和初始化

    一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...

随机推荐

  1. Codeforces 86D Powerful array (莫队)

    D. Powerful array time limit per test 5 seconds memory limit per test 256 megabytes input standard i ...

  2. php面试专题---18、MySQL查询优化考点

    php面试专题---18.MySQL查询优化考点 一.总结 一句话总结: 慢查询:查找分析查询速度慢的原因 数据访问:优化查询过程中的数据访问 长难句:优化长难的查询语句 特定类型:优化特定类型的查询 ...

  3. HTTP和HTTPS协议,详解

    大纲 一.前言: 先来观察这两张图,第一张访问域名http://www.12306.cn,谷歌浏览器提示不安全链接,第二张是https://kyfw.12306.cn/otn/regist/init, ...

  4. EZOJ #375高速公路

    分析 我们可以先跑一遍全价的最短路 之后我们枚举这个第k大的价格w[i] 将其它边减这个边的权值和0取max 在跑出最短路之后加上减去的费用,即w[i]*k 我们发现如果价值大于w[i]的边小于k个 ...

  5. AGC024C Sequence Growing Easy

    题目大意 你开始有一个序列x 它所有项都是0 你有一个操作:x[i]=x[i-1]+1 问你至少几次操作可以让x序列变为给定的a序列 分析 老年人完全不会这种脑子题/kk........ 我们定义b[ ...

  6. day03—JavaScript中DOM的Event事件方法

    转行学开发,代码100天——2018-03-19 1.Event 对象 Event 对象代表事件的状态,比如事件在其中发生的元素.键盘按键的状态.鼠标的位置.鼠标按钮的状态. 事件通常与函数结合使用, ...

  7. linux RZ 命令

    root 账号登陆后,依次执行以下命令: 1 cd /tmp 2 wget http://www.ohse.de/uwe/releases/lrzsz-0.12.20.tar.gz 3 tar zxv ...

  8. NYOJ 654喜欢玩warcraft的ltl(01背包/常数级优化)

    传送门 Description ltl 非常喜欢玩warcraft,因为warcraft十分讲究团队整体实力,而他自己现在也为升级而不拖累团队而努力. 他现在有很多个地点来选择去刷怪升级,但是在每一个 ...

  9. JUnit的基本使用

    一些关于单元测试的理念:    单元测试并不能证明你的代码是正确的,只能证明你的代码是没有错误的. Keep bar green and keep your code cool    关于JUnit的 ...

  10. 16、NumPy ——字节交换

    NumPy 字节交换 在几乎所有的机器上,多字节对象都被存储为连续的字节序列.字节顺序,是跨越多字节的程序对象的存储规则. 大端模式:指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地 ...