ConcurrentHashMap是如何提高并发时的吞吐性能
为并发吞吐性能所做的优化
ConcurrentHashMap使用了一些技巧来获取高的并发性能,同时避免了锁。这些技巧包括:
- 为不同的Hash bucket(所谓hash bucket即不同范围的key的hash值)使用多个写锁;
- 利用JMM(Java Memory Model,java内存模型)的不确定性使得持有锁的时间最小化,或者从根本上避免使用锁。
ConcurrentHashMap为最常用的场景进行了优化,比如获取一个已经存在于Map中的值。事实上,绝大多数成功的get()操作在运行中根本就没有使用锁,这是因为利用了JMM的不确定性。
多个写锁
回忆一下HashTable的线程安全是因为使用了一个单独的全部Map范围的锁,这个锁在所有的插入、删除、查询操作中都会持有,甚至在使用
Iterator遍历整个Map时也会持有这个单独的锁。当锁被一个线程持有时,就能够防止其他线程访问该Map,即便其他线程都处于闲置状态。这种单个
锁的机制极大的限制了并发的性能。
而ConcurrentHashMap抛弃了仅仅使用整个Map范围的一个锁的机制,取而代之的是使用了32个锁,每个锁会负责hash
bucket的一个子集(即负责一部分key的hash值范围)。而且这些锁仅仅被那些更改Map内容的操作使用,比如put(),
remove()。拥有32个单独的锁意味时最多可以有32个线程同时修改Map,这并不是说如果有少于32个线程同时修改Map而没有线程被阻塞,32
仅仅是理论上的并发写操作的极限,在实际中一般不一定会达到。在目前的软硬件条件下,对多数程序而言,32个锁总比一个锁要好。
Map范围的锁
32个独立的锁,每个锁负责hash
bucket的一个子集,这意味着如果有些排他的访问操作,就需要获得全部的32个锁,比如rehashing(即扩充hash
bucket的数量,当HashMap的key增长时重新分布key元素)就必须是排他的访问。但是JAVA语言本身没有提供一种简单的方式来获取变长的
锁,由于这种操作不会频繁发生,那么ConcurrentHashMap是使用递归实现Map范围的锁。
JMM(Java内存模型)概览
JMM就是Java内存模型,它定义了Java的线程之间如何通过内存进行交互。简单说就是: JVM中存在一个主内存(Main Memory
or Java Heap Memory),JAVA中所有变量都储存在主存中,对于所有线程都是共享的。每个线程都有自己的工作内存(Working
Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主
存完成。工作内存里的变量, 在多核CPU的情况下, 将大部分储存于处理器高速缓存中, 高速缓存在不经过内存时,
所以线程之间也是不可见的。详细的关于JMM的介绍会在后面的文章中。
大家都知道,CPU尽量使用自己的寄存器内部的数据,这样会极大的提高性能。JAVA规范(JLS-Java Language
Specification)中规定了一些内存操作不必立即被其他线程发现,并且还提供了两个语言层面的机制来确保在多个线程之间保持内存操作的一致
性:synchronized和volatile。根据JSL中的描述"In the absence of explicit
synchronization, an implementation is free to update the main memory in
an order that may be surprising."
(在没有显示的同步时,一个操作可以以一种令人惊讶的方式自由的更新主存。)这意思是说:没有同步时,在一个线程中写操作的操作顺序也许和另外一个线程的
写操作顺序不一样,并且更新内存变量会在不确定的时间之后被其他线程发现。
而使用synchronized的最根本的原因就是确保线程访问关键代码段的原子性。synchronized实际上提供了三个功能
atomicity, visibility, and
ordering(原子性,可见性和顺序行)。所谓原子性很好理解也很直接,就是确保不同线程再次进入同一区域的互斥性,防止在同一时刻有多于一个线程可
以访问到被保护的代码段。很不幸的是许多的文章只强调了synchronized的原子性方面,而不说其他两个方面。在JMM中,同步扮演了一个重要的角
色,当获取和释放monitor(锁)的时候,同步操作使得JVM执行了内存屏障(execute memory barriers)。
当一个线程获取一个锁时,它便执行了一次内存读屏障(read
barriers)——所谓内存屏障就是使得任何其他线程缓存的本地内存(CPU内部缓存或者CPU寄存器)无效,然后使得其他所有线程所在的CPU重新
从主存(内存)读取这些变量的值。同样,在释放锁的时候,所有其他线程均执行了一次内存写屏障(write
barriers)——Flush任何已经更改的变量到主存(内存)。互斥和内存屏障的结合意味着只要程序遵循正确的同步规则(这个同步规则就是:同步任
何被写的变量会下次被另一个线程正确的读;或者是同步任何读一个下一次会被另一个线程正确的更改变量),每个线程都会看到它所使用的共享变量的正确值。
如果在访问共享变量时没有同步,你会碰到一些奇怪的事情,一些改变会很快的反应到其他线程里,而其他一些更改则需要花费一些时间才能反应到其他线
程中。这样的结果就会使得你如果不用synchronized,那么你不能确保你看到一致的内存视图(即相关变量在不同的线程中的值会不一致,也许有一些
值是脏数据)。通常的方法,也是推荐的方法去避免这些脏数据当然是正确采用synchronized。在这种情况下,比如在广泛使用的基础类
ConcurrentHashMap中就值得使用一些额外的专门知识和功夫去开发,以获得高性能。
ConcurrentHashMap是如何提高并发时的吞吐性能的更多相关文章
- java ConcurrentHashMap和CopyOnWriteArrayList解决并发问题
ConcurrentHashMap 一.hashtable.hashmap.ConcurrentHashMap 1.线程不安全的HashMap 因为多线程环境下,使用Hashmap进行put操作会引起 ...
- SQL Server数据库读写分离提高并发性
在一些大型的网站或者应用中,单台的SQL Server 服务器可能难以支撑非常大的访问压力.很多人在这时候,第一个想到的就是一个解决性能问题的利器——负载均衡.遗憾的是,SQL Server 的所有版 ...
- STM 软件事务内存——本质是为提高并发,通过事务来管理内存的读写访问以避免锁的使用
对Java程序员来说,我们对面向对象的编程(OOP)自然都是烂熟于胸的,但语言也极大地影响了我们构建面向对象应用程序的方式.(现在的OOP已经和Alan Kay当初创造这个词时候的初衷大不相同了,他的 ...
- ecshop 秒杀并发时库存会被减到小于0的解决办法
ecshop 秒杀并发时库存会被减到小于0更新库存后,再进行库存检查,如果库存为负数,则执行事务的回滚. begin();//开始一个事物处理开始 $sql = "UPDATE " ...
- 关于sphinx+PHP在高并发时响应性能低下的解决办法
经过多次压力测试,发现sphinx在高并发时出现负载突然提升,并且响应速度明显下降.经过多方面的排查,发现是由于PHP与sphinx自带的 searchd进行socket的连接之后,系统内存有大量的T ...
- FluorineFx 播放FLV 时堆棧溢出解决 FluorineFx NetStream.play 并发时,无法全部连接成功的解决办法
http://25swf.blogbus.com/tag/FluorineFx/ http://www.doc88.com/p-7002019966618.html 基于Red5的视频监控系统的研究 ...
- [转]你如何面对—LNMP高并发时502
From : http://www.topthink.com/topic/5683.html 之前php-fpm配置: 单个php-fpm实例,使用socket方式,内存8G 静态方式,启动php-f ...
- Linux的虚拟内存管理-如何分配和释放内存,以提高服务器在高并发情况下的性能,从而降低了系统的负载
Linux的虚拟内存管理有几个关键概念: Linux 虚拟地址空间如何分布?malloc和free是如何分配和释放内存?如何查看堆内内存的碎片情况?既然堆内内存brk和sbrk不能直接释放,为什么不全 ...
- j2ee高并发时使用全局变量需要注意的问题
原文:https://blog.csdn.net/jston_learn/article/details/21617311 开发中,全局变量的使用很频繁,但对于多线程的访问,使用全局变量需要注意的地方 ...
随机推荐
- 根据werservice代码用CXF生成WSDL
原文:http://hongyegu.iteye.com/blog/619147,谢谢! import org.apache.cxf.tools.java2ws.JavaToWS; import ne ...
- Kotlin语法(基础)
一.基础语法: 1. 定义包名: 包名应该在源文件的最开头,包名不必和文件夹路径一致:源文件可以放在任意位置. package my.demo 2. 定义函数: fun sum(a: Int , b: ...
- WAMP集成环境的安装
暑假已经正式开始,我的学习计划也开始有了初步的进展,今天学习的主要内容是PHP的基础知识,以及在电脑上面安装了集成的WAMP(Windows+Apache+MySQL+PHP). PHP的基础知识: ...
- iOS Address Book指南
尽管OC是一门面向对象的语言,但是在你做开发的时候你会发现,并不是所有你用的frameworks都是面向对象的.有些是用C写的,例如Address Book的API,接下来让我们去学习一下Addres ...
- iOS开发--Swift 基于AFNetworking 3.0的网络请求封装
Swift和OC基于AFNetworking的网络请求流程相同, 就是语法不同, 对于Swift语法不是很清楚的同学, 建议多看看API文档, 自己多多尝试. 写过OC的应该都明白每句话做什么的, 就 ...
- Mac下载安装Android Studio教程
今天把公司闲置的一台Mac-mini重装了下系统感觉用着速度还不错,平时上班用的机器USB有些问题,所以打算用这台Mac.以往开发用Intellij Idea就够用,但是这次项目引用的jar包太多,遭 ...
- Swift 二维码扫描 简单实现
3.30看视频 学到了二维码简单的实现 还有一些动画的实现 今天就先记录一下二维码扫描的简单实现 不太好记手写一遍 学习的基础在于模仿嘛 创建一个实现二维码扫描的步骤 1.首先是懒加载创建 会话 ...
- Java基础知识学习(九)
GUI开发 先前用Java编写GUI程序,是使用抽象窗口工具包AWT(Abstract Window Toolkit).现在多用Swing.Swing可以看作是AWT的改良版,而不是代替AWT,是对A ...
- pentaho cde数据联动,下拉框,文本框,图形
先看一下效果: 开源bi工具pentaho数据联动,和传统意义上的更改数据不同,pentaho cde 需要一个监听来动态传值. 说一下需要注意的几个地方吧 1.参数是不能在两个图表中直接传递的,必须 ...
- SQL Server 2012实施与管理实战指南(笔记)——Ch4数据库连接组件
4.数据库连接组件 访问数据库有多种不同的技术,包括ADO,ODBC,OLEDB,ADO.NET等这些都有一些共性.首先要建立连接(Connection),然后通过命令(Command)对数据库进行访 ...