Java并发—同步容器和并发容器
简述同步容器与并发容器
在Java并发编程中,经常听到同步容器、并发容器之说,那什么是同步容器与并发容器呢?同步容器可以简单地理解为通过synchronized来实现同步的容器,比如Vector、Hashtable以及SynchronizedList等容器,如果有多个线程调用同步容器的方法,它们将会串行执行。
可以通过查看Vector、Hashtable等同步容器的实现代码,可以看到这些容器实现线程安全的方式就是将它们的状态封装起来,并在需要同步的方法上加上关键字synchronized,但在某些情况下,同步容器不一定就是线程安全的,比如获取最后一个元素或者删除最后一个元素,我们需要实现额外的同步操作:
public static Object getLast(Vector list) {
int lastIndex = list.size() - 1;
return list.get(lastIndex);
} public static void deleteLast(Vector list) {
int lastIndex = list.size() - 1;
list.remove(lastIndex);
}
虽然上面的方法看起来没有问题,Vector自身的方法也是同步的,但是在多线程环境中还是隐藏着问题。如果有两个线程A,B同时调用上面的两个方法,假设list的大小为10,这里计算得到的lastIndex为9,线程B首先执行了删除操作(多线程之间操作执行的不确定性导致),而后线程A调用了list.get方法,这时就会发生数组越界异常,导致问题的原因就是上面的复合操作不是原子操作,这里可以通过在方法内部使用list对象锁来实现原子操作。
同步容器会导致多个线程中对容器方法调用的串行执行,降低并发性,因为它们都是以容器自身对象为锁,所以在需要支持并发的环境中,可以考虑使用并发容器来替代。并发容器是针对多个线程并发访问而设计的,在jdk5.0引入了concurrent包,其中提供了很多并发容器,如ConcurrentHashMap、CopyOnWriteArrayList等。
其实同步容器与并发容器都为多线程并发访问提供了合适的线程安全,不过并发容器的可扩展性更高。在Java5之前,程序员们只有同步容器,且在多线程并发访问的时候会导致争用,阻碍了系统的扩展性。Java5介绍了并发容器,并发容器使用了与同步容器完全不同的加锁策略来提供更高的并发性和伸缩性。例如,在ConcurrentHashMap中采用了一种粒度更细的加锁机制,可以称为分段锁,在这种锁机制下,允许任意数量的读线程并发地访问map,并且执行读操作的线程和写操作的线程也可以并发的访问map,同时允许一定数量的写操作线程并发地修改map,所以它可以在并发环境下实现更高的吞吐量,另外,并发容器提供了一些在使用同步容器时需要自己实现的复合操作,包括putIfAbsent等,但是由于并发容器不能通过加锁来独占访问,所以我们无法通过加锁来实现其他复合操作了。
并发容器
由上面的分析我们知道,同步容器并不能保证多线程安全,而并发容器是针对多个线程并发访问而设计的,在jdk5.0引入了concurrent包,其中提供了很多并发容器,极大的提升同步容器类的性能。
ConcurrentHashMap
- 对应的非并发容器:HashMap
- 目标:代替Hashtable、synchronizedMap,支持复合操作
- 原理:JDK6中采用一种更加细粒度的加锁机制Segment“分段锁”,JDK8中采用CAS无锁算法,详细分析推荐阅读【JDK】:ConcurrentHashMap高并发机制——【转载】。
CopyOnWriteArrayList
- 对应的非并发容器:ArrayList
- 目标:代替Vector、synchronizedList
- 原理:利用高并发往往是读多写少的特性,对读操作不加锁,对写操作,先复制一份新的集合,在新的集合上面修改,然后将新集合赋值给旧的引用,并通过volatile 保证其可见性,当然写操作的锁是必不可少的了。
关于这一部分可参考【JDK】:CopyOnWriteArrayList、CopyOnWriteArraySet 源码解析
CopyOnWriteArraySet
- 对应的费并发容器:HashSet
- 目标:代替synchronizedSet
- 原理:基于CopyOnWriteArrayList实现,其唯一的不同是在add时调用的是CopyOnWriteArrayList的addIfAbsent方法,其遍历当前Object数组,如Object数组中已有了当前元素,则直接返回,如果没有则放入Object数组的尾部,并返回。
关于这一部分可参考【JDK】:CopyOnWriteArrayList、CopyOnWriteArraySet 源码解析
ConcurrentSkipListMap
- 对应的非并发容器:TreeMap
- 目标:代替synchronizedSortedMap(TreeMap)
- 原理:Skip list(跳表)是一种可以代替平衡树的数据结构,默认是按照Key值升序的。Skip list让已排序的数据分布在多层链表中,以0-1随机数决定一个数据的向上攀升与否,通过”空间来换取时间”的一个算法。ConcurrentSkipListMap提供了一种线程安全的并发访问的排序映射表。内部是SkipList(跳表)结构实现,在理论上能够在O(log(n))时间内完成查找、插入、删除操作。
ConcurrentSkipListSet
- 对应的非并发容器:TreeSet
- 目标:代替synchronizedSortedSet
- 原理:内部基于ConcurrentSkipListMap实现
ConcurrentLinkedQueue
不会阻塞的队列
- 对应的非并发容器:Queue
- 原理:基于链表实现的FIFO队列(LinkedList的并发版本)
LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue
- 对应的非并发容器:BlockingQueue
- 特点:拓展了Queue,增加了可阻塞的插入和获取等操作
- 原理:通过ReentrantLock实现线程安全,通过Condition实现阻塞和唤醒
- 实现类:
- LinkedBlockingQueue:基于链表实现的可阻塞的FIFO队列
- ArrayBlockingQueue:基于数组实现的可阻塞的FIFO队列
- PriorityBlockingQueue:按优先级排序的队列
参考:
Java并发—同步容器和并发容器的更多相关文章
- JAVA同步容器和并发容器
同步容器类 同步容器类的创建 在早期的JDK中,有两种现成的实现,Vector和Hashtable,可以直接new对象获取: 在JDK1.2中,引入了同步封装类,可以由Collections.sync ...
- Java并发——同步容器与并发容器
同步容器类 早期版本的JDK提供的同步容器类为Vector和Hashtable,JDK1.2 提供了Collections.synchronizedXxx等工程方法,将普通的容器继续包装.对每个共有方 ...
- 【Java并发编程二】同步容器和并发容器
一.同步容器 在Java中,同步容器包括两个部分,一个是vector和HashTable,查看vector.HashTable的实现代码,可以看到这些容器实现线程安全的方式就是将它们的状态封装起来,并 ...
- Java并发--同步容器
为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch).今天我们就来讨论下同步容器. ...
- Java并发(9)- 从同步容器到并发容器
引言 容器是Java基础类库中使用频率最高的一部分,Java集合包中提供了大量的容器类来帮组我们简化开发,我前面的文章中对Java集合包中的关键容器进行过一个系列的分析,但这些集合类都是非线程安全的, ...
- java多线程总结-同步容器与并发容器的对比与介绍
1 容器集简单介绍 java.util包下面的容器集主要有两种,一种是Collection接口下面的List和Set,一种是Map, 大致结构如下: Collection List LinkedLis ...
- Java线程同步类容器和并发容器(四)
同步类容器都是线程安全的,在某些场景下,需要枷锁保护符合操作,最经典ConcurrentModifiicationException,原因是当容器迭代的过程中,被并发的修改了内容. for (Iter ...
- Java并发-同步容器篇
作者:汤圆 个人博客:javalover.cc 前言 官人们好啊,我是汤圆,今天给大家带来的是<Java并发-同步容器篇>,希望有所帮助,谢谢 文章如果有问题,欢迎大家批评指正,在此谢过啦 ...
- java并发编程笔记3-同步容器&并发容器&闭锁&栅栏&信号量
一.同步容器: 1.Vector容器实现了List接口,Vector实际上就是一个数组,和ArrayList类似,但是Vector中的方法都是synchronized方法,即进行了同步措施.保证了线程 ...
随机推荐
- 一次csrf配合xss的攻击实例
xss出现在我的邮箱处 alert of payload "><script>alert(/test/)</script><" by cooki ...
- debian下为arm开发板创建基于debian或emdebian的根文件系统
Debian系统本身包含对arm的支持,其包含的软件包最多,但是最终的文件系统要大一些. emdebian 是一个非常好用的嵌入式linux操作系统,其基于debian的特点对于熟悉debian系统的 ...
- 3G模块(U6300)linux下拨号上网
U6300支持linux.Android系列嵌入式系统.作为linux内核系统,系统均会自带驱动usbserial,就没有提供专门的U6300V的USB驱动,都是加载系统的usbserial以实现对U ...
- 【shell】tar命令详解
tar [-cxtzjvfpPN] 文件与目录 ....参数:-c :建立一个压缩文件的参数指令(create 的意思):-x :解开一个压缩文件的参数指令!-t :查看 tarfile 里面的文件! ...
- Linux上创建SSH隧道
Win上有好用的Xshell,可以做SSH隧道,但是Linux没有很好用的工具,本来gSTM还可以,但是死活装不上,也很久没更新了. 但其实,Linux上直接使用ssh命令就可以创建SSH隧道,非常方 ...
- web html 防盗链
一概念 1防盗链 在HTTP协议中,有一个表头字段叫referer,采用URL的格式来表示从哪儿链接到当前的网页或文件,通过referer,网站可以检测目标网页访问的来源网页.有了referer跟踪来 ...
- javascript基础语法及使用
前几年自学过JavaScript,由于从事安卓开发,就放弃了对js的学习,今天又捡起来重新学习了下,希望对大家有所帮助. 首先介绍下什么是JavaScript. JavaScript 是互联网上最流行 ...
- DB2 SQL Error: SQLCODE=-668, SQLSTATE=57016错误解决方法
这个错误是:表处于"装入暂挂"状态. 经多次尝试 总结方法: 1:reorg table <表>: 假如不好使 则下面方法 2,先前尝试装入(LOAD)此表失败.表的状 ...
- 上传图片到数据库,读取数据库中图片并显示(C#)
from:http://blog.csdn.net/bfcady/article/details/2622701 思路:建立流对象,将上传图片临时保存到byte数组中,再用SQL语句将其保存到数据库中 ...
- DICOM:docker实现DICOM服务虚拟化
背景: docker,是一个开源的应用容器引擎,眼下大多应用在部署和运维领域,然而因为全然使用沙箱机制,相互之间能够看做独立的主机,且自身对资源的需求也十分有限.远远低于虚拟机.甚至非常多时候.能够直 ...