首页 / JAVA / Java并发 -- 并发容器
Java并发 -- 并发容器
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java并发 -- 并发容器,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含4474字,纯文字阅读大概需要7分钟。
内容图文
![Java并发 -- 并发容器](/upload/InfoBanner/zyjiaocheng/823/18c5f96837814e9da1e537d992fdfb37.jpg)
同步容器
- Java 1.5之前提供的同步容器虽然也能保证线程安全,但性能很差
- Java中的容器主要分为四大类,分别为List、Map、Set和Queue,并不是所有的Java容器都是线程安全的
- 将非线程安全的容器变成线程安全的容器的简单方案:synchronized
- 把非线程安全的容器封装在对象内部,然后控制好访问路径即可
如果有想学习java/大数据的程序员,可来我们的java、大数据学习扣qun:589348389,
免费送java、大数据的视频教程噢!
我每晚上8点还会在群内直播讲解Java、大数据知识,欢迎大家前来学习哦。
线程安全的ArrayList
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class SafeArrayList<T> { private List<T> list = new ArrayList<>(); public synchronized T get(int idx) { return list.get(idx); } public synchronized void add(int idx, T t) { list.add(idx, t); } public synchronized boolean addIfNotExist(T t) { if (!list.contains(t)) { list.add(t); return true; } return false; } } |
Collections.synchronized
1 2 3 |
Collections.synchronizedList(new ArrayList()); Collections.synchronizedSet(new HashSet()); Collections.synchronizedMap(new HashMap()); |
组合操作存在竟态条件问题
- 上面的addIfNotExist就包含组合操作
- 组合操作往往隐藏着竟态条件问题,即便每个操作都能保证原子性,也不能保证组合操作的原子性
- 用迭代器遍历同步容器也存在竟态条件问题,因为组合操作不具备原子性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// 存在竟态条件问题 List<Object> list = Collections.synchronizedList(new ArrayList<>()); Iterator<Object> iterator = list.iterator(); while (iterator.hasNext()) { process(iterator.next()); } // 并发安全,先锁住list再执行遍历操作 List<Object> list = Collections.synchronizedList(new ArrayList<>()); synchronized (list) { Iterator<Object> iterator = list.iterator(); while (iterator.hasNext()) { process(iterator.next()); } } |
并发容器
- Java在1.5之前所谓的线程安全容器,主要指的是同步容器
- 同步容器最大的问题是性能差,所有方法都用synchronized来保证互斥,串行度太高
- 在Java 1.5提供了性能更高的容器,称为并发容器
分类
并发容器数量众多,但依旧可以分成四大类:List、Map、Set和Queue
List
- List里面只有一个实现类就是CopyOnWriteArrayList
- CopyOnWrite即在执行写操作的时候会将共享变量重新复制一份出来,这样的好处是读操作是完全无锁的
- CopyOnWriteArrayList内部维护一个数组,成员变量array指向这个内部数组,所有的读操作都是基于array进行的
- 如果在遍历array的同时,还有一个写操作
- 会将array复制一份,然后在新复制的数组上执行写操作,执行完之后再将array指向这个新的数组
- 因此读写是并行的, 遍历操作一直都是基于原array执行的,而写操作则是基于新array执行的
- 应用场景:仅适用于写操作非常少的场景,而且能够容忍读写的短暂不一致
- CopyOnWriteArrayList的迭代器是只读的,不支持增删改,因为对快照进行增删改是没有意义的
Map
- Map接口的两个实现:ConcurrentHashMap和ConcurrentSkipListMap
- ConcurrentHashMap的key是无序的,而ConcurrentSkipListMap的key是有序的
- ConcurrentSkipListMap里面的SkipList本身是一种数据结构,翻译成跳表
- 跳表执行插入、删除和查询操作的平均复杂度为O(log n)
- 理论上与并发线程数无关,适用于并发度非常高的情况(ConcurrentHashMap的性能也不能满足要求)
集合类 | Key | Value | 线程安全 |
---|---|---|---|
HashMap | 允许为null | 允许为null | 否 |
TreeMap | 不允许为null | 允许为null | 否 |
HashTable | 不允许为null | 不允许为null | 是 |
ConcurrentHashMap | 不允许为null | 不允许为null | 是 |
ConcurrentSkipListMap | 不允许为null | 不允许为null | 是 |
Set
- Set接口的两个实现:CopyOnWriteArraySet和ConcurrentSkipListSet
- 原理与CopyOnWriteArrayList和ConcurrentSkipListMap类似
Queue
- JUC中的Queue类的并发容器是最复杂的,可以从两个维度分类,阻塞/非阻塞、单端/双端
- 阻塞/非阻塞:阻塞指的是当队列已满时,入队操作阻塞;当队列已空时,出队操作阻塞
- 单端/双端:单端指的是只能队尾入队,队首出队;双端指的是队首队尾皆可出队入队
- 在JUC中,阻塞队列用Blocking关键字标识,单端队列用Queue标识,双端队列用Qeque标识
单端阻塞队列
- 其实现包括
- ArrayBlockingQueue
- LinkedBlockingQueue
- SynchronousQueue
- LinkedTransferQueue
- PriorityBlockingQueue
- DelayQueue
- 内部一般都会持有一个队列
- 该队列可以是数组(ArrayBlockingQueue)
- 也可以是链表(LinkedBlockingQueue)
- 甚至不持有队列(SynchronousQueue),生产者线程的入队操作必须等待消费者线程都出队操作
- LinkedTransferQueue融合了LinkedBlockingQueue和SynchronousQueue的功能,性能比LinkedBlockingQueue更好
- PriorityBlockingQueue支持按优先级出队
- DelayQueue支持延时队列
双端阻塞队列
单端非阻塞队列
其实现是ConcurrentLinkedQueue
双端非阻塞队列
其实现是ConcurrentLinkedDeque
是否有界
- 使用队列时,要格外注意队列是否支持有界
- 实际工作中,一般不建议使用无界的队列,因为有可能会导致OOM
- 上面提到的Queue,只有ArrayBlockingQueue和LinkedBlockingQueue是支持有界的
内容总结
以上是互联网集市为您收集整理的Java并发 -- 并发容器全部内容,希望文章能够帮你解决Java并发 -- 并发容器所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。