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).今天我们就来讨论下同步容器. ...
随机推荐
- C# 打开模态对话框 和打开文件夹
C# 打开另一个窗体,(模态对话框) Form1 frm= new Form1(); //创建对象 DialogResult retServer = frm.ShowDialog(); //模式对话框 ...
- java数据类型和码表、转义字符
类型名称 字节空间 范围 整数型 byte 1 -27到27-1 或者 -128到127 short 2 -215到215-1 int 4 -231到231-1 long 8 ...
- java中一个数组不能放不同数据类型的值
在java中,数组不能放不同数据类型的值. 方法一: 多态 定义数组类型的时候定义为父类,而存进数组为父类的子类 public class test2 { public static void mai ...
- The JVM Architecture Explained
转自:https://dzone.com/articles/jvm-architecture-explained?oid=18544920 Every Java developer knows tha ...
- 18清明校内测试T3
扫雷(mine) Time Limit:1000ms Memory Limit:128MB 题目描述 rsy最近沉迷于一款叫扫雷的游戏. 这个游戏是这样的.一开始网格上有n*m个位置,其中有一些位 ...
- Vector 二维数组 实现
1.C++实现动态二维数组 int **p; p = ]; //注意,int*[10]表示一个有10个元素的指针数组 ; i < ; ++i) { p[i] = ]; } 2.利用指针数组实现二 ...
- 基于vue的nuxt框架cnode社区服务端渲染
nuxt-cnode 基于vue的nuxt框架仿的cnode社区服务端渲染,主要是为了seo优化以及首屏加载速度 线上地址 http://nuxt-cnode.foreversnsd.cngithub ...
- 关于read和fread
1.fread与read的区别---open和fopen的区别--fread函数和fwrite函数:http://blog.csdn.net/dreamtdp/article/details/7560 ...
- 手机访问pc版网站自动跳转为手机版页面
1.PC版首页</head>标签前加上以下脚本 <script src="/tools/browser_redirect.ashx"></script ...
- 中国省市区地址三级联动插件---jQuery Distpicker
插件描述:distpicker是一款可以实现中国省市区地址三级联动jQuery插件.它使用简单,简单设置即可完成中国省市区地址联动效果. [官网]https://fengyuanchen.github ...