背景:并发知识是一个程序员段位升级的体现,同样也是进入BAT的必经之路,有必要把并发知识重新梳理一遍。

ConcurrentHashMap:
在有了并发的基础知识以后,再来研究concurrent包。普通的HashMap为非线程安全的,在高并发场景下要使用线程安全版本的ConcurrentHashMap;

众所周知HashTable可以保证线程安全但却效率低下,而HashMap是非线程安全但效率却高于HashTable,于是ConcurrentHashMap就孕育而生成为二者的结合体,为了更好的理解ConcurrentHashMap先看下这两个Map。

HashMap

HashMap之所以具有很快的访问速度,因为它是根据键的hashCode值来存储数据,在大多数情况下可以直接定位到它的值,但遍历的顺序是不确定的;HashMap的key可以为null,但是最多只允许一条记录的键为null,另外允许多条记录(value)的值为null,key为null的键值对永远都放在一table[0]为头节点的链表中;HashMap为非线程安全的,适用于单线程环境下,即在任一时刻都可以有多个线程同时对HashMap进行读或写操作,可以会导致数据的不一致;如果一定要使用HashMap又要保证线程安全,则可以用Collection的synchronizedMap方法或ConcurrentHashMap都OK;HashMap是基于哈希实表实现的,每一个元素是一个key-value对,其内部通过单链表结局冲突问题的,当Map容量不足(超过了阀值)时链表会自动增长;HashMap实现了Serializable接口,因此其支持序列化,并且实现了Cloneable接口,可以被克隆;

HashMap存储数据的过程:

HashMap内部维护了一个存储数据的Entry数组,HashMap采用链表解决冲突,每一个Entry本质上其实是一个单向链表;当要添加一个key-value对时,首先会通过hash(key)方法技术hash值,然后通过indexFor(hash,length)求该key-value对的存储位置,其计算方法是先用Hash&0x7FFFFFFF后,再对length取模,这就保证了每一个key-value对都能存入HashMap,当计算出相同的位置是,由于存入位置是一个链表,所以把这个key-value对插入链表头。

如上图1 所示,最左边竖列排的多个方格就代表哈希表,也叫哈希数组,数组的每个元素都是一个单链表的头节点,链表是用来解决冲突的,如果不同的key映射到了数组的同一位置处,就将其放入单链表中;HashMap内存储数据的Entry数组默认是16,如果没有对Entry扩容机制的话,当存储的数据一多,Entry内部的链表会很长,这就失去了HashMap的存储意义了,所以HashMap内部有自己的扩容机制(当size大于threshold时,对HashMap进行扩容)。 上图2 是HashMap的链表存储结构,其中E*代表一个Node节点,每个Node节点就对应着一个key-value的mapping映射;每个Node除了保存了key和value的映射之外,还保存了它下一Node的引用(Eb保存了Ebb的引用,而Ebb保存了Ebbb的引用);图2中,每一个链表如Ec-->Ecc-->Eccc,这三个节点的key是不相等的。

分析HashMap源码会发现其内部有几个重要的变量如:size用于记录HashMap的底层数组中已用槽的数量、threshold用于HashMap的阈值判断,看是否需要调整HashMap的容量(threshold = 容量*加载因子)、DEFAULT_LOAD_FACTOR = 0.75f,即加载因子默认0.75。 HashMap的扩容是是新建了一个HashMap的底层数组,通过调用transfer方法,将就HashMap的全部元素添加到新的HashMap中(此步需要重新计算元素在新的数组中的索引位置,导致HashMap扩容成为一个相当耗时的操作),So我们在用HashMap的时,最好能提前预估下HashMap中元素的个数,这样有助于提高HashMap的性能。

HashTable
HashTable的功能与HashMap类似,如同样是基于哈希表实现的、内部也是通过单链表解决冲突问题、容量不足时也会自动增加、同样实现了Seriablizable接口支持序列化、实现了Cloneable接口可克隆;不同的是HashTable继承自Dictionary类且为线程安全的(任一时间只有一个线程可以写HashTable,但性能不如

ConcurrentHashMap),而HashMap继承AbstractMap类且非线程安全。

如图3,HashTable只有一把锁,当一个线程访问HashTable的同步方法时,会将整张table 锁住,当其他线程也想访问HashTable 同步方法时,就会进入阻塞或轮询状态。也就是确保同一时间只有一个线程对同步方法的占用,避免多个线程同时对数据的修改,由此确保线程的安全性;但HashTable 对get,put,remove 方法都使用了同步操作,这就造成如果两个线程都只想使用get 方法去读取数据时,因为一个线程先到进行了锁操作,另一个线程就不得不等待,这样必然导致效率低下,而且竞争越激烈,效率越低下。

ConcurrentHashMap(并发且线程安全)

ConcourrentHashMap是通过分段锁技术来保证线程安全的[case:一个人到酒店开房可直接在前台办理入住,三个陌生人到酒店开房登记入住,另外两个则要先排队等第一个办理结束(普通的Map),要是三个人所住的每个楼层都有一个可以办理入住的前台就无需排队了(ConcurrentHashMap)];ConcurrentHashMap主要由Segment(桶)和HashEntry(节点)两大数据组成,如下图:

在hashMap 的基础上,ConcurrentHashMap将数据分为多个segment(默认16个),然后每次操作对一个segment 加锁,HashTable 在竞争激烈的并发环境下表现出效率低下的原因是由于所有访问HashTable的线程都必须竞争同一把锁,而ConcurrentHashMap将数据分到多个segment 中(默认16,也可在申明时自己设置,不过一旦设定就不能更改,扩容都是扩充各个segment 的容量),由于每个segment 都有一个自己的锁,只要多个线程访问的不是同一个segment 就没有锁争用,就没有堵塞,也就是允许16个线程并发的更新并且不存在锁争用现象。除此之外,ConcurrentHashMap的segment就类似一个HashTable,但比HashTable又更进一步优化,因为HashTable对get,put,remove方法都会使用锁,而ConcurrnetHashMap中get方法是不涉及到锁的;并且ConcurrentHashMap内部在并发读取时,除了key 对应的value为null的情况下会用到锁,其它的场景下都没有用到锁,所以对于读操作无论多少线程并发都是安全高效的。

并发concurrent---3的更多相关文章

  1. Java并发---concurrent包

    一.包的结构层次 其中包含了两个子包atomic和locks,另外字concurrent下的阻塞队列以及executor,这些就是concurrent包中的精华.而这些类的实现主要是依赖于volati ...

  2. 并行parallel和并发concurrent的区别

    http://stackoverflow.com/questions/1050222/concurrency-vs-parallelism-what-is-the-difference Concurr ...

  3. java 并发 concurrent Executor

    Excutor类 Executor 执行提交的对象Runnable任务. ExecutorService 一个Executor ,提供方法来管理终端和方法,可以产生Future为跟踪一个或多个异步任务 ...

  4. Java虚拟机6:内存溢出和内存泄露、并行和并发、Minor GC和Full GC、Client模式和Server模式的区别

    前言 之前的文章尤其是讲解GC的时候提到了很多的概念,比如内存溢出和内存泄露.并行与并发.Client模式和Server模式.Minor GC和Full GC,本文详细讲解下这些概念的区别. 内存溢出 ...

  5. iOS 处理多个网络请求的并发的情况

    如何处理多个网络请求的并发的情况 一.概念 1.并发 当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配 ...

  6. 并发concurrent---2

    背景:并发知识是一个程序员段位升级的体现,同样也是进入BAT的必经之路,有必要把并发知识重新梳理一遍. 并发concurrent: 使用ThreadLocal可以实现线程范围内共享变量,线程A写入的值 ...

  7. 并发concurrent---1

    背景:并发知识是一个程序员段位升级的体现,同样也是进入BAT的必经之路,有必要把并发知识重新梳理一遍. 并发concurrent: 说到并发concurrent,肯定首先想到了线程,创建线程有两种方法 ...

  8. python多进程并发和多线程并发和协程

    为什么需要并发编程? 如果程序中包含I/O操作,程序会有很高的延迟,CPU会处于等待状态,这样会浪费系统资源,浪费时间 1.Python的并发编程分为多进程并发和多线程并发 多进程并发:运行多个独立的 ...

  9. java并发编程概念

    并发:当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其 ...

  10. 《C#并发编程经典实例》学习笔记-关于并发编程的几个误解

    误解一:并发就是多线程 实际上多线程只是并发编程的一种形式,在C#中还有很多更实用.更方便的并发编程技术,包括异步编程.并行编程.TPL 数据流.响应式编程等. 误解二:只有大型服务器程序才需要考虑并 ...

随机推荐

  1. STM32学习笔记(二):GPIO口工作原理

    STM32每个IO口具有7个寄存器来控制,每个IO口都可以自由进行编程控制,我们编程实际上控制的是通过控制那7个寄存器来控制我们的IO口,我们可以通过编程控制IO口,把IO口配置成如下八种模式: 1. ...

  2. 『Lucas定理以及拓展Lucas』

    Lucas定理 在『组合数学基础』中,我们已经提出了\(Lucas\)定理,并给出了\(Lucas\)定理的证明,本文仅将简单回顾,并给出代码. \(Lucas\)定理:当\(p\)为质数时,\(C_ ...

  3. xamarin开发android收集的一些工具

    xamarin开发android收集的一些工具 工欲善其事,必先利其器,从16年下半年开始做xamarin相关的开发,平时使用的一些工具和google插件给大家分享一下,都有下载地址,持续更新. Vi ...

  4. 使用 ASP.NET Core MVC 创建 Web API(五)

    使用 ASP.NET Core MVC 创建 Web API 使用 ASP.NET Core MVC 创建 Web API(一) 使用 ASP.NET Core MVC 创建 Web API(二) 使 ...

  5. C#处理json实战

    昨天看到技术群中发了一个查询天气的api,http://www.sojson.com/open/api/weather/json.shtml?city=南昌 点进去看,发现服务器传回来一个天气信息的j ...

  6. 设计模式之创建类模式PK

    创建类模式包括: 工厂方法模式 建造者模式 抽象工厂模式 单例模式 原型模式 创建类模式能够提供对象的创建和管理职责. 其中单例模式和原型模式非常容易理解, 单例模式是要保持在内存中只有一个对象,原型 ...

  7. h5实现实时时钟

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <meta nam ...

  8. Spring之AOP详解

    文章大纲 一.AOP介绍二.Spring的AOP实战三.AOP常用标签四.项目源码及参考资料下载五.参考文章   一.AOP介绍 1. 什么是AOP 在软件业,AOP为Aspect Oriented ...

  9. vba读文本如果文本文件太大会提示错误!

    Sub 文本文件太大会提示错误() Dim TT, p Open "I:\xxxxx\yyyzz.txt" For Input As #1       '读取txt文件 Do Wh ...

  10. Exp2后门原理与实践 20164312马孝涛

    实验内容 使用netcat获取主机操作Shell,cron启动 (0.5分) 使用socat获取主机操作Shell, 任务计划启动 (0.5分) 使用MSF meterpreter(或其他软件)生成可 ...