JAVA 多线程随笔 (三) 多线程用到的并发容器 (ConcurrentHashMap,CopyOnWriteArrayList, CopyOnWriteArraySet)
1.引言
在多线程的环境中,如果想要使用容器类,就需要注意所使用的容器类是否是线程安全的。在最早开始,人们一般都在使用同步容器(Vector,HashTable),其基本的原理,就是针对容器的每一个操作,都添加synchronized来进行同步,此种方式尽管简单,但是其性能是非常地下的,所以现在已经不怎么使用了。人们普遍会使用并发的容器,在JDK1.5之后,针对基于散列的Map,提供了新的ConcurrentHashMap,针对迭代需求的list,提供了CopyOnWriteList.
2.ConcurrentHashMap
ConcurrentHashMap使用了一种分段锁的策略,使得map可以被多个读写线程并行的访问。基本可以认为是将map的key值范围分为多个段,这样多个线程访问的时候,他们需要访问的key值在不同的段,所以可以互相不干扰,
使用不同的锁对象来进行并发操作。
ConcurrentHashMap在使用迭代器遍历的时候,不会报ConcurrentModificationException,提供“弱一致性”。在遍历迭代的时候,也会反应出在迭代器创建之后的数据修改。
应用场景
针对一般的有并发需求的map,都应该使用ConcurrentHashMap. 它的性能优于Hashtable和synchronizedMap。
缺点
1.不是强一致性
由于是采用的分段锁策略,所以一些数据不能保证强一致性。比如针对容器的size方法,由于线程A只是获得了自己的分段锁,它不能保证其他线程对容器的修改,所以此时线程A可能使用size,会得到不稳定数据。这种情况下,是对同步性能的一些折衷。如果业务需求必须满足强一致性,才会需要对整个Map进行锁操作。并发容器的弱一致性的概念背景,是在高并发情况下,容器的size和isEmpty之类的方法,用处不大,所以可以忍受数据不一致性。
3.CopyOnWrite容器
在JDK1.5之后,java.util.concurrent引入了两个CopyOnWrite容器,分别是CopyOnWriteArrayList, CopyOnWriteArraySet.
顾名思义,CopyOnWrite就是在write操作之前,对集合进行Copy,针对容器的任意改操作(add,set,remove之类),都是在容器的副本上进行的。并且在修改完之后,将原容器的引用指向修改后的副本。
如果线程A得到容器list1的iterator之后,线程B对容器list1加入了新的元素,由于线程A获得list1的iterator时候在线程B对list1进行修改前,所以线程A是看不到线程B对list1进行的任何修改。
具体到源码,看一下add操作
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
可以发现,写操作是会有个锁lock.lock(),这保证了多线程写操作之间的同步。之后使用Arrays.copyOf来进行数组拷贝,在修改完成后,setArray(newElements)将原来的数组引用指向新的数组。
应用场景
经常用在读多写少的场景,比如EventListener的添加,网站的category列表等偶尔修改,但是需要大量读取的情景。
缺点
1.数据一致性的问题。
因为读操作没有用到并发控制,所以可能某个线程读到的数据不是实时数据。
2.内存占用问题。
因为写操作会进行数据拷贝,并且旧有的数据引用也可能被其他线程占有一段时间,这样针对数据比较大的情况,可能会占用相当大的内存。并且由于每次写操作都会占用额外的内存,最后进行的GC时间也可能相应的增加。
JAVA 多线程随笔 (三) 多线程用到的并发容器 (ConcurrentHashMap,CopyOnWriteArrayList, CopyOnWriteArraySet)的更多相关文章
- Java多线程_并发容器ConcurrentHashMap/CopyOnWriteArrayList/CopyOnWriteArraySet
ConcurrentHashMap HashMap是线程不安全的,可以使用Collections.synchronizedMap(map)把一个不安全的map变成安全的,但是这里可以直 ...
- Java编程的逻辑 (74) - 并发容器 - ConcurrentHashMap
本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...
- 多线程之并发容器ConcurrentHashMap(JDK1.6)
简介 ConcurrentHashMap 是 util.concurrent 包的重要成员.本文将结合 Java 内存模型,分析 JDK 源代码,探索 ConcurrentHashMap 高并发的具体 ...
- 多线程之并发容器ConcurrentHashMap
这部分内容转载自: http://www.haogongju.net/art/2350374 JDK5中添加了新的concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步 ...
- Java并发编程:并发容器ConcurrentHashMap
Java并发编程:并发容器之ConcurrentHashMap(转载) 下面这部分内容转载自: http://www.haogongju.net/art/2350374 JDK5中添加了新的concu ...
- Java并发容器--ConcurrentHashMap
引子 1.不安全:大家都知道HashMap不是线程安全的,在多线程环境下,对HashMap进行put操作会导致死循环.是因为多线程会导致Entry链表形成环形数据结构,这样Entry的next节点将永 ...
- 深入理解Java并发容器——ConcurrentHashMap
目录 重要属性和类 put 为什么java8后放弃分段锁,改用CAS和同步锁 初始化 addCount 扩容 树化 参考 重要属性和类 sizeCtl 容量控制标识符,在不同的地方有不同用途,而且它的 ...
- Java并发(9)- 从同步容器到并发容器
引言 容器是Java基础类库中使用频率最高的一部分,Java集合包中提供了大量的容器类来帮组我们简化开发,我前面的文章中对Java集合包中的关键容器进行过一个系列的分析,但这些集合类都是非线程安全的, ...
- Java并发容器篇
作者:汤圆 个人博客:javalover.cc 前言 断断续续一个多月,也写了十几篇原创文章,感觉真的很不一样: 不能说技术有很大的进步,但是想法确实跟以前有所不同: 还没开始的时候,想着要学的东西太 ...
随机推荐
- python:轮播图
下载jquery.bxslider 参考地址:www.bxslider.com 引入jquery.bxslider.css和jquery.bxslider.js <!DOCTYPE HTML P ...
- COUNT(1)和COUNT(*)区别
项目经常用到count(1),但是和count(*)什么区别? 从下面实验结果来看,Count (*)和Count(1)查询结果是一样的,都包括对NULL的统计,而count(列名) 是不包括NULL ...
- Bootstrap Affix(附加导航(Affix)插件的用法)
原文网址:http://www.runoob.com/bootstrap/bootstrap-affix-plugin.html Bootstrap 附加导航(Affix)插件 附加导航(Affix) ...
- C# 正则表达式小坑 -- not enough
今天遇到了一个奇怪的报错: 相关的代码是这样的: Regex regex = new Regex("(?<=wwwroot\\).*?$"); 然后把这段正则复制到任何正则测 ...
- laravel框架总结(八) -- ORM模型
ORM模型简介 1>什么是ORM? ORM,即 Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在操作具体的 ...
- iniutils for Golang
一直有计划将 Delphi 中的譬如 TIniFile 等相关功能移植到 Golang,这些设施在 Delphi 中(相对而言)比较常用,使用起来也非常方便. 虽然 Github 上早已有这些三方库, ...
- tinkphp验证码的使用
页面显示验证码: <div class="input-group has-feedback "> <input id="yzm" type=& ...
- 8.mvc core上传文件
以下方法均是个人,仅供参考 public interface IFileHelper { /// <summary> /// 保存文件 (返回 Test.jpg) 出错就返回 error| ...
- ionic imgBase64
navigator.camera.getPicture(onPhotoDataSuccess, onFail, { quality: , destinationType: destinationTyp ...
- python 给定n,返回n以内的斐波那契数列
方式一:函数 def fabs(n): a, b = 0, 1 while b < n: print(b, end=' ') a, b = b, a+b fabs(1000) 方式二:列表 re ...