作者主页:paper jie_博客
本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。
本文于《JavaEE》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将基础知识一网打尽,希望可以帮到读者们哦。
其他专栏:《MySQL》《C语言》《javaSE》《数据结构》等
内容分享:本期将会分享线程安全的集合类芝士
目录
引入
多线程使用ArrayList
多线程使用队列
多线程使用哈希表
Hashtable
ConcurrentHashMap
相关面试题
之前我们所学的集合类,大多数都是线程不安全的.,像ArrayList,LinkedList,Queue等都是线程不安全的.这里我们将介绍`线程安全的类
1.可以自己使用synchronized来加锁实现线程安全
2.Collections.synchronizedList(new ArrayList);
它就相当于给ArrayList套了一个壳,通过这个壳得到了一个新的对象,这个新的对象的关键方法就加上了synchronized.
3.使用CopyOnWritArrayList
当我们放容器中添加元素时,不会往旧容器中添加,而是会将当前这个容器的数据拷贝到一个新的容器中.添加完元素后,在将原有容器的引用指向新的容器.
它带来的好处就是我们可以对CopyWritArrayList容器进行并发的读,不需要加锁,因为当前容器不会添加任何元素.
优点就是在读多写少的场景下,性能很高,不需要加锁.
缺点就是比较占用内存,新写的数据不能第一时间读到.且不适用与写多的场景.
1.ArrayListBlockingQueue
2.LinkedListBlockingQue
3.priorityBlockingQueue
4.TransferQueue
只包含一个元素的阻塞队列.
在数据结构中,我们学过hashmap和hashset,这两者本身其实是线程不安全的.在所线程的环境下我们就可以使用Hashable和CouncurrentHashMap.
而我们的Hashtable就是在关键方法中加上了synchronized关键字,这其实就是直接对Hashtable对象本身直接加锁.这就会出现一些问题:
如果多个线程访问同一个Hashtable就会造成锁冲突.
size属性也是被synchronized控制,这样锁冲突会进一步加大.
一但触发扩容,就会由该线程来完成扩容过程,这个过程机会涉及到大量的元素拷贝,这里的速度就会很慢.
一个Hashtable就只有一个锁,只要有两个线程访问这个HashTable中的任意一个数据就会发生锁竞争.这里读操作和其他链表的元素是不会发生线程安全问题的,但是这里还是会有锁.
相比于Hashtable,ConcurrentHashMap就做出了一系列的改进和优化.这里以Java1.8为例:
1. 读操作没有加锁,只是使用了volatile保证从内存读取结果,只对写操作进行加锁.加锁的方式仍然是使用synchronized,只不过它加锁的不是整个对象,而是"锁桶",这就是每一个链表,这里用每个链表的头节点来作为锁对象,这样就大大降低了锁冲突的概率.
2. 充分的利用了CAS特性.size属性就是使用CAS来更新的,这样就又降低了锁冲突的概率.
3. 优化了扩容方法,采用的是化整为零.
发现需要扩容的线程,只需要创建出一个数组,再搬运少量元素过去即可.
扩容期间,新老数组同时存在
后面每个操作ConcurrentMap的线程都会参与搬运数组的任务,每个操作都会搬运一小部分
直到搬运完最后一个元素再将老数组删除.
这个期间插入元素只往新数组中插入.
这个期间查找删除元素新数组和老数组都需要查找和删除.
1. ConcurrentHashMap的读是否要加锁?
读不需要加锁,这样可以减少锁冲突.但是为了及时读到刚修改的数据,搭配了volatile关键字.
2. 介绍ConcurrentHashMap的分段技术?
这是Java1.7中采用的技术.Java1.8中已经不再使用了.它就是将若干个链表分成一个段,给段加上锁,目的还是为了降低锁竞争的概率.当两个线程访问的数据在一个段中就会发生锁竞争.
3.ConcurrentHashMap在jdk1.8做了哪些优化?
取消了分段锁,直接给每个哈希桶每个链表分配了一个锁.将原来数组+链表的实现方式改变为数组+链表+红黑树的方式.当链表大于等于8时就会由链表转变为红黑树.
4.Hashtable和HashMap,ConcurrentHashMap的区别?
HashMap: 线程不安全,key可以为null
Hashtable: 线程安全,使用synchronized锁加在Hashtable对象上,效率低.且key不可以为null
ConcurrentHashMap: 线程安全,使用synchronized锁加在每个链表上,锁冲突率低,充分利用CAS机制,优化了扩容方式,key不能为null.