Java同步容器总结
《0》StringBuffer适用于多线程场景,StringBuilder适用于字符串拼接【堆栈封闭】
`Vector`实现`List`接口,底层和`ArrayList`类似,但是`Vector`中的方法都是使用`synchronized`修饰,即进行了同步的措施。 但是,`Vector`并不是线程安全的。
`Stack`也是一个同步容器,也是使用`synchronized`进行同步,继承与`Vector`,是数据结构中的,先进后出。
`HashTable`和`HashMap`很相似,但`HashTable`进行了同步处理。
`Collections`工具类提供了大量的方法,比如对集合的排序、查找等常用的操作。同时也通过了相关了方法创建同步容器类
如果在使用foreach或iterator进集合的遍历,尽量不要在操作的过程中进行remove等相关的更新操作。如果非要进行操作,则可以在遍历的过程中记录需要操作元素的序号,待遍历结束后方可进行操作,让这两个动作分开进行
同步容器中的方法主要采取synchronized进行同步,因此执行的性能会收到受到影响,并且同步容器并不一定能做到真正的线程安全。
《1》CopyOnWriteArrayList
ArrayList -> CopyOnWriteArrayList
CopyOnWriteArrayList相比于ArrayList是线程安全的,从字面意思理解,即为写操作时复制。CopyOnWriteArrayList使用了一种叫写时复制的方法,当有新元素添加到CopyOnWriteArrayList时,先从原有的数组中拷贝一份出来,然后在新的数组做写操作,写完之后,再将原来的数组引用指向到新数组。
CopyOnWriteArrayList的整个add操作都是在锁的保护下进行的。 这样做是为了避免在多线程并发add的时候,复制出多个副本出来,把数据搞乱了,导致最终的数组数据不是我们期望的。
CopyOnWtiteArrayList.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(); } }
缺点:由于写操作的时候,需要拷贝数组,会消耗内存,如果原数组的内容比较多的情况下,可能导致young gc或者full gc;
不能用于实时读的场景,像拷贝数组、新增元素都需要时间,所以调用一个set操作后,读取到数据可能还是旧的,虽然CopyOnWriteArrayList能做到最终一致性,但是还是没法满足实时性要求;
总结:CopyOnWriteArrayList 合适读多写少的场景,不过这类慎用 因为谁也没法保证CopyOnWriteArrayList 到底要放置多少数据,万一数据稍微有点多,每次add/set都要重新复制数组,这个代价实在太高昂了。
设计思想:读写分离,读和写分开;最终一致性。最终保证List的结果是对的;使用另外开辟空间的思路,来解决并发冲突
CopyOnWriteArrayList并发的读,则分几种情况:
如果写操作未完成,那么直接读取原数组的数据;
如果写操作完成,但是引用还未指向新数组,那么也是读取原数组数据;
如果写操作完成,并且引用已经指向了新的数组,那么直接从新数组中读取数据。
《2》CopyOnWriteArraySet
HashSet -> CopyOnWriteArraySet
CopyOnWriteArraySet底层实现是采用CopyOnWriteArrayList,适合比较小的集合,其中所有可变操作(add、set、remove等等)都是通过对底层数组进行一次新的复制来实现的,一般需要很大的开销。迭代器支持hasNext(), next()等不可变操作,不支持可变的remove操作;使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。
《3》ConcurrentSkipListSet
底层使用ConcurrentSkipListMap,TreeSet -> ConcurrentSkipListSet
ConcurrentSkipListSet<E>和TreeSet一样,都是支持自然排序,并且可以在构造的时候定义Comparator<E>的比较器,该类的方法基本和TreeSet中方法一样(方法签名一样)
和其他的Set集合一样,ConcurrentSkipListSet<E>都是基于Map集合的,ConcurrentSkipListMap便是它的底层实现
在多线程的环境下,ConcurrentSkipListSet<E>中的contains、add、remove操作是安全的,多个线程可以安全地并发执行插入、移除和访问操作。但是对于批量操作addAll、removeAll、retainAll 和 containsAll并不能保证以原子方式执行。理由很简单,因为addAll、removeAll、retainAll底层调用的还是contains、add、remove的方法,在批量操作时,只能保证每一次的contains、add、remove的操作是原子性的(即在进行contains、add、remove三个操作时,不会被其他线程打断),而不能保证每一次批量的操作都不会被其他线程打断。因此,在addAll、removeAll、retainAll 和 containsAll操作时,需要添加额外的同步操作。
此类不允许使用 null 元素,因为无法可靠地将 null 参数及返回值与不存在的元素区分开来。
《4》ConcurrentHashMap
HashMap -> ConcurrentHashMap,不允许null值,绝大部分使用Map都是读取操作,而且读操作大多数都是成功的,因此,ConcurrentHashMap针对读操作进行了大量的优化。在高并发的场景下,有很大的优势。
因为多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap。 HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容数组中,在多线程的环境下,存在同时其他的元素也在进行put操作,如果hash值相同,可能出现同时在同一数组下用链表表示,造成闭环,导致在get时会出现死循环,所以HashMap是线程不安全的。
HashTable,它是线程安全的,它在所有涉及到多线程操作的都加上了synchronized关键字来锁住整个table,这就意味着所有的线程都在竞争一把锁,在多线程的环境下,它是安全的,但是无疑是效率低下的。
其实HashTable有很多的优化空间,锁住整个table这么粗暴的方法可以变相的柔和点,比如在多线程的环境下,对不同的数据集进行操作时其实根本就不需要去竞争一个锁,因为他们不同hash值,不会因为rehash造成线程不安全,所以互不影响,这就是锁分离技术,将锁的粒度降低,利用多个锁来控制多个小的table,多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率,这就是ConcurrentHashMapJDK1.7版本的核心思想。
《5》ConcurrentSkipListMap
TreeMap -> ConcurrentSkipListMap,内部使用``SkipList`结构实现的。跳表是一个链表,但是通过使用“跳跃式”查找的方式使得插入、读取数据时复杂度变成了O(log n)。
跳表(SkipList):使用“空间换时间”的算法,令链表的每个结点不仅记录next结点位置,还可以按照level层级分别记录后继第level个结点。
concurrentHashMap与ConcurrentSkipListMap性能测试
在4线程1.6万数据的条件下,ConcurrentHashMap 存取速度是ConcurrentSkipListMap 的4倍左右。
但ConcurrentSkipListMap有几个ConcurrentHashMap不能比拟的优点:
ConcurrentSkipListMap 的key是有序的,而ConcurrentHashMap是做不到的
ConcurrentSkipListMap 支持更高的并发。ConcurrentSkipListMap的存取时间是log(N),和线程数几乎无关。也就是说在数据量一定的情况下,并发的线程越多,ConcurrentSkipListMap越能体现出他的优势。
在非多线程情况下,尽量使用TreeMap,此外,对于并发性较低的程序,可以使用Collections工具所提供的方法synchronizedSortMap,它是将TreeMap进行包装。对于高并发场景下,应使用`ConcurrentSkipListMap`提供更高的并发度。并且,如果在多线程环境下,需要对`Map`的键值进行排序时,也要尽量使用ConcurrentSkipListMap。
《6》安全共享策略总结
以下策略是通过线程安全策略中的不可变对象、线程封闭、同步容器以及并发容器相关知识总结而得:
线程限制:一个被线程限制的对象,由线程独占,并且只能被占有它的线程修改
共享只读:一个共享只读的对象,在没有额外同步的情况下,可以被多个线程并发访问,但是任何线程都不能修改它
线程安全对象:一个线程安全的对象或容器,在内部通过同步机制来保证线程安全,所以其他线程无需额外的同步就可以通过公共接口随意访问它
被守护对象:被守护对象只能通过获取特定的锁来访问
Java同步容器总结的更多相关文章
- JAVA同步容器和并发容器
同步容器类 同步容器类的创建 在早期的JDK中,有两种现成的实现,Vector和Hashtable,可以直接new对象获取: 在JDK1.2中,引入了同步封装类,可以由Collections.sync ...
- Java同步容器
一.为什么会出现同步容器 Java的集合框架中,主要有四大类别:List,Set,Queue,Map List,Set,Queue接口分别继承了Collection接口,Map本身是一个接口. 注意C ...
- Java 同步容器和并发容器
同步容器(在并发下进行迭代的读和写时并不是线程安全的) Vector.Stack.HashTable Collections类的静态工厂方法创建的类(如Collections.synchr ...
- 转:java多线程--同步容器
java同步容器 在Java的集合容器框架中,主要有四大类别:List.Set.Queue.Map.List.Set.Queue接口分别继承了Collection接口,Map本身是一个接口.注意Col ...
- Java并发编程:同步容器
Java并发编程:同步容器 为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch). ...
- 【JAVA并发编程实战】3、同步容器
同步容器包括Vector和Hashtable,还有一些由Collections.synchronizedXxx等工厂方法创建的 1.同步容器类的问题 同步容器类都是线程安全的,但是有些时候还是要客户端 ...
- java并发:同步容器&并发容器
第一节 同步容器.并发容器 1.简述同步容器与并发容器 在Java并发编程中,经常听到同步容器.并发容器之说,那什么是同步容器与并发容器呢?同步容器可以简单地理解为通过synchronized来实现同 ...
- Java并发——同步容器与并发容器
同步容器类 早期版本的JDK提供的同步容器类为Vector和Hashtable,JDK1.2 提供了Collections.synchronizedXxx等工程方法,将普通的容器继续包装.对每个共有方 ...
- 【转】Java并发编程:同步容器
为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch).今天我们就来讨论下同步容器. ...
随机推荐
- 如何给PDF文件添加水印?
在数字化媒体高速发展的今天,信息传播的速度也越来越快,人们常常会在网络上一些有趣的图片,文件,段子诸如此类的东西,人们往往会去下载或转发,但是因为一些因素,导致版权之经常上演,水印呢,其实就给你自己的 ...
- parseInt()函数
parseInt()转化整形是从左往右,取出第一个整型,如:10a10b,则显示10: //20170719补充 如果该字符串第一个字符是0,那么该字符串会基于八进制而非十进制来求值,在八进制中,8和 ...
- 新建springcloud 找不到请求路径
新建 启动类 controller 都不可以直接放在 java 目录下 否则启动失败
- 获取webconfig配置文件内容
string ServerUrl= ConfigurationManager.AppSettings["ServerUrl"].ToString(); web.config中的配置 ...
- 16.1 foreach 循环中捕获变量的变化
在 foreach 循环内的匿名函数(通常为Lambda表达式)中捕获循环 变量时要格外小心.代码清单16-1就展示了这样一个简单的示例,它看上去似乎会输出 x . y . z . string[] ...
- python最好用的IDE及查看源码的方法
一.PyCharm 很多语言都有比较流行的开发工具,比如JAVA 的Eclipse, C#,C++的VisualStudio,最好的Python 开发IDE就是PyCharm 可以直接调试代码,试运行 ...
- 洛谷 2042 BZOJ 1500 NOI 2005 维护数列
[题意概述] 维护一个数列,要求支持以下6种操作: [题解] 大Boss...可以用Treap解决 需要用到垃圾回收.线性建树. #include<cstdio> #include< ...
- SCU Travel
Travel The country frog lives in has n towns which are conveniently numbered by 1,2,…,n . Among n(n− ...
- hdu_1863_畅通工程_201403122000
畅通工程 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- Lifting the Stone 计算几何 多边形求重心
Problem Description There are many secret openings in the floor which are covered by a big heavy sto ...