JAVA提高二十:CopyOnWriteArrayList&CopyOnWriteArraySet&ConcurrentHashMap介绍
前面我们将java集合类的大部分类都进行了深入分析,但我们会发现一个共性问题就是并发的问题,那么如何解决呢?我们前面基本都是通过Collections的一个工具类来进行的解决,但实际大部分使用中人们普遍会使用并发的容器,在JDK1.5之后,针对基于散列的Map,提供了新的ConcurrentHashMap,针对迭代需求的list,提供了CopyOnWriteList.。因此这里我进行下简单介绍和分析,具体的原理实现将在并发学习中进行详细的介绍。
一、集合总结
我们前面学习了很多集合类,这里我们做一个简单的总结,先整体了解类的关系,如下图所示:

大部分我们应该都有过介绍了,下面我们回顾一些重点知识:
| 关 注 点 | 结 论 |
| ArrayList是否允许空 | 允许 |
| ArrayList是否允许重复数据 | 允许 |
| ArrayList是否有序 | 有序 |
| ArrayList是否线程安全 | 非线程安全 |
| 关 注 点 | 结 论 |
| LinkedList是否允许空 | 允许 |
| LinkedList是否允许重复数据 | 允许 |
| LinkedList是否有序 | 有序 |
| LinkedList是否线程安全 | 非线程安全 |
| 关 注 点 | 结 论 |
| HashMap是否允许空 | Key和Value都允许为空 |
| HashMap是否允许重复数据 | Key重复会覆盖、Value允许重复 |
| HashMap是否有序 | 无序,特别说明这个无序指的是遍历HashMap的时候,得到的元素的顺序基本不可能是put的顺序 |
| HashMap是否线程安全 | 非线程安全 |
| 关 注 点 | 结 论 |
| LinkedHashMap是否允许键值对为空 | Key和Value都允许空 |
| LinkedHashMap是否允许重复数据 | Key重复会覆盖、Value允许重复 |
| LinkedHashMap是否有序 | 有序 |
| LinkedHashMap是否线程安全 | 非线程安全 |
二、CopyOnWriteArrayList&CopyOnWriteArraySet&ConcurrentHashMap
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时间也可能相应的增加。
最后,集合的介绍知识到此结束,感谢博客园的各位园友们,下面的学习将进入IO/Nio的学习。
参考资料:
http://wiki.jikexueyuan.com/project/java-collection/linkedhashset.html
https://www.cnblogs.com/skywang12345
http://blog.csdn.net/column/details/java-collections.html
https://www.cnblogs.com/xrq730
http://www.cnblogs.com/xiaoxi/category/929860.html
JAVA提高二十:CopyOnWriteArrayList&CopyOnWriteArraySet&ConcurrentHashMap介绍的更多相关文章
- Java同步数据结构之CopyOnWriteArrayList/CopyOnWriteArraySet
前言 前面介绍完了队列(包括双端队列),今天探讨以下Java并发包中一个List的并发数据结构实现CopyOnWriteArrayList,顾名思义CopyOnWriteArrayList也是一种基于 ...
- JAVA提高二:枚举
JDK5.0中有一个非常有用的特性:枚举,这个特性以前在C语言中出现过,后来JDK出现后,开始觉得没有必要,但随着使用JAVA语言的人数增多,发现大家对枚举的需求非常大,于是又加入了此特性,下面我们来 ...
- JAVA 多线程随笔 (三) 多线程用到的并发容器 (ConcurrentHashMap,CopyOnWriteArrayList, CopyOnWriteArraySet)
1.引言 在多线程的环境中,如果想要使用容器类,就需要注意所使用的容器类是否是线程安全的.在最早开始,人们一般都在使用同步容器(Vector,HashTable),其基本的原理,就是针对容器的每一个操 ...
- JAVA多线程学习十六 - 同步集合类的应用
1.引言 在多线程的环境中,如果想要使用容器类,就需要注意所使用的容器类是否是线程安全的.在最早开始,人们一般都在使用同步容器(Vector,HashTable),其基本的原理,就是针对容器的每一个操 ...
- Java设计模式(十二) 策略模式
原创文章,同步发自作者个人博客,http://www.jasongj.com/design_pattern/strategy/ 策略模式介绍 策略模式定义 策略模式(Strategy Pattern) ...
- Nodejs学习笔记(十六)--- Pomelo介绍&入门
目录 前言&介绍 安装Pomelo 创建项目并启动 创建项目 项目结构说明 启动 测试连接 聊天服务器 新建gate和chat服务器 配置master.json 配置servers.json ...
- Java进阶(二十五)Java连接mysql数据库(底层实现)
Java进阶(二十五)Java连接mysql数据库(底层实现) 前言 很长时间没有系统的使用java做项目了.现在需要使用java完成一个实验,其中涉及到java连接数据库.让自己来写,记忆中已无从搜 ...
- Java9发布回顾Java 8的十大新特性
java9已经在北京时间9月22日正式发布,开发者可以在oracle jdk官网上下载到最新的jdk9. 今天,我们先来一起复习一下2014年发布的Java 8的十大新特性.先来喝杯java~~~ 按 ...
- Nodejs学习笔记(十六)—Pomelo介绍&入门
前言&介绍 Pomelo:一个快速.可扩展.Node.js分布式游戏服务器框架 从三四年前接触Node.js开始就接触到了Pomelo,从Pomelo最初的版本到现在,总的来说网易出品还算不错 ...
随机推荐
- SQL Server多表同时查询
今天在练sql server发现多条语句同时使用可以多表同时查询,具体操作如下: 代码示例: USE teachingGOSELECT *FROM dbo.teach_classORDER BY cl ...
- Linux运维正则表达式之grep
一.什么是正则表达式?简单的说,正则表达式就是一套处理大量的字符串而定义的规则和方法.例如:假设 @代表12345通过正则表达式这些特殊符号,我们可以快速过滤.替换需要的内容.linux正则表达式一般 ...
- [验证码识别技术]字符验证码杀手--CNN
字符验证码杀手--CNN 1 abstract 目前随着深度学习,越来越蓬勃的发展,在图像识别和语音识别中也表现出了强大的生产力.对于普通的深度学习爱好者来说,一上来就去跑那边公开的大型数据库,比如I ...
- WebWorker实战使用
总体来说webworker解决了阻塞主线程问题,但是还没解决高性能计算的问题 WebWorker整体介绍 https://developer.mozilla.org/zh-CN/docs/Web/AP ...
- 在ERP中定义用户时报错:SqlDateTime 溢出。必须介于 1/1/1753 12:00:00 AM 和 12/31/9999 11:59:59 PM 之间
在ERP中定义用户时. 报错: SqlDateTime 溢出.必须介于 1/1/1753 12:00:00 AM 和 12/31/9999 11:59:59 PM 之间. 原因分析: ①没有正确初 ...
- 从实战出发,谈谈 nginx 信号集
前言 之前工作时候,一台引流测试机器的一个 ngx_lua 服务突然出现了一些 HTTP/500 响应,从错误日志打印的堆栈来看,是不久前新发布的版本里添加的一个 Lua table 不存在,而有代码 ...
- Python入门 - 时间处理
本讲主要介绍python时间处理方式,比如获取当前的时间: import time ftime = time.strftime("%Y年%m月%d日%H点%M分%S秒", time ...
- RGBA 和 opacity的区别
两者都可以设置透明度 区别 RGBA 只影响当前元素 opacity 后代会继承该css 值,暂时还没有办法清除该css 在线演示
- oracle 数据库中的序列
序列是什么,通俗点说,序列就是按照一定顺序进行排列,序列会自动给你递增,生成唯一的序列号: oracle数据库不同于sqlServer数据库,oracle数据库中是没有自增长列,使用的是sequenc ...
- 前端测试框架Jest系列教程 -- Global Functions(全局函数)
写在前面: Jest中定义了很多全局性的Function供我们使用,我们不必再去引用别的包来去实现类似的功能,下面将列举Jest中实现的全局函数. Jest Global Functions afte ...