[并发编程] -- 容器和框架篇
时间:2020-06-30
本文章向大家介绍
[并发编程] -- 容器和框架篇
,主要包括
[并发编程] -- 容器和框架篇
使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
-
ConcurrentHashMap
的实现原理与使用ConcurrentHashMap
是线程安全且高效的HashMap
。- 为什么要使用
ConcurrentHashMap
。- jdk1.7的HashMap可能导致程序死循环:多线程会导致
HashMap
的Entry
链表形成环形数据结构。而jdk1.8引入红黑树的数据结构和扩容的优化。
优化内容具体可参照 https://tech.meituan.com/2016/06/24/java-hashmap.html
- 使用线程安全的
HashTable
效率又非常低下:使用synchronized
来保证线程安全,但在线程竞争激烈
的情况下HashTable
的效率非常低下。(jdk1.7,建议弃用) ConcurrentHashMap
的锁分段技术可有效提升并发访问率:首先将数据分成一段一段地存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
- jdk1.7的HashMap可能导致程序死循环:多线程会导致
-
ConcurrentHashMap
的结构 - ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。
- Segment是一种可重入锁(ReentrantLock),在ConcurrentHashMap里扮演锁的角色。
- HashEntry则用于存储键值对数据。
- ConcurrentHashMap的类图
- 结构图
public V get(Object key) {
int hash = hash(key.hashCode());
return segmentFor(hash).get(key, hash);
}
-
get操作的高效之处在于整个get过程不需要加锁,除非读到的值是空才会加锁重读。
- get方法里将要使用的共享变量都定义成
volatile
类型,能够在线程之间保持可见性,能够被多线程同时读,并且保证不会读到过期的值,但是只能被单线程写(有一种情况可以被多线程写,就是写入的值不依赖于原值),在get操作里只需要读不需要写共享变量count和value,所以可以不用加锁; - 之所以不会读到过期的值,是因为根据Java内存模型的happen before原则,对
volatile
字段的写入操作先于读操作,即使两个线程同时修改和获取volatile
变量,get操作也能拿到最新的值。
- get方法里将要使用的共享变量都定义成
-
由于put方法里需要对共享变量进行写入操作,所以为了线程安全,在操作共享变量时必须加锁。
- 判断是否需要对Segment里的HashEntry数组进行扩容
- 定位添加元素的位置,然后将其放在HashEntry数组里。
-
size操作,先尝试2次通过不锁住Segment的方式来统计各个Segment大小,如果统计的过程中,容器的count发生了变化,则再采用加锁的方式来统计所有Segment的大小。
- 使用modCount变量,在put、remove和clean方法里操作元素前都会将变量modCount进行加1,那么在统计size前后比较modCount是否发生变化,从而得知容器的大小是否发生变化。
-
ConcurrentLinkedQueue
基于链接节点的无界线程安全队列。- 采用先进先出的规则对节点进行排序。
- 当我们获取一个元素时,它会返回队列头部的元素。
- 采用了“wait-free”算法(即CAS算法)来实现,该算法在Michael&Scott算法上进行了一些修改。
ConcurrentLinkedQueue
类图
- Java中的阻塞队列
- 阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法
- 支持阻塞的插入方法:意思是当队列满时,队列会阻塞插入元素的线程,直到队列不满。
- 支持阻塞的移除方法:意思是在队列为空时,获取元素的线程会等待队列变为非空
阻塞队列使用场景:生产者跟消费者。
- 在阻塞队列不可用时,这两个附加操作提供了4种处理方式
- 抛出异常:当队列满时,如果再往队列里插入元素,会抛出IllegalStateException("Queue full")异常。当队列空时,从队列里获取元素会抛出NoSuchElementException异常。
- 返回特殊值:当往队列插入元素时,会返回元素是否插入成功,成功返回true。如果是移除方法,则是从队列里取出一个元素,如果没有则返回null。一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到队列可用或者响应中断退出。当队列空时,如果消费者线程从队列里take元素,队列会阻塞住消费者线程,直到队列不为空。
- 超时退出:当阻塞队列满时,如果生产者线程往队列里插入元素,队列会阻塞生产者线程一段时间,如果超过了指定的时间,生产者线程就会退出。
- jdk 7 提供了7个阻塞队列
ArrayBlockingQueue
:一个由数组
结构组成的有界阻塞
队列,默认不保证线程公平的访问队列,可设置公平性,公平性是使用可重入锁
实现。public ArrayBlockingQueue(int capacity, boolean fair) { if (capacity <= 0) throw new IllegalArgumentException(); this.items = new Object[capacity]; lock = new ReentrantLock(fair); notEmpty = lock.newCondition(); notFull = lock.newCondition(); }
LinkedBlockingQueue
:一个由链表
结构组成的有界阻塞
队列。PriorityBlockingQueue
:一个支持优先级
排序的无界阻塞
队列。DelayQueue
:一个使用优先级
队列实现的无界阻塞
队列。使用场景:- 缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。
- 定时任务调度:使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,比如TimerQueue就是使用DelayQueue实现的。
SynchronousQueue
:一个不存储
元素的阻塞
队列。它支持公平访问队列。默认情况下线程采用非公平性策略访问队列。适合传递性场景。吞吐量
高于LinkedBlockingQueue和ArrayBlockingQueue。public SynchronousQueue(boolean fair) { transferer = fair new TransferQueue() : new TransferStack(); }
LinkedTransferQueue
:一个由链表
结构组成的无界阻塞
队列。LinkedBlockingDeque
:一个由链表
结构组成的双向阻塞
队列。
- 阻塞队列的实现原理
- 通知模式:当生产者往满的队列里添加元素时会阻塞住生产者,当消费者消费了一个队列中的元素后,会通知生产者当前队列可用。
- Fork/Join框架
- 工作窃取算法:是指某个线程从其他队列里窃取任务来执行。
- 优点:充分利用线程进行并行计算,减少了线程间的竞争。
- 缺点:在某些情况下还是存在竞争,比如双端队列里只有一个任务时。并且该算法会消耗了更多的系统资源,比如创建多个线程和多个双端队列。
- 工作窃取算法:是指某个线程从其他队列里窃取任务来执行。
原文地址:https://www.cnblogs.com/lycsmzl/p/13213534.html
- 【学术】独热编码如何在Python中排列数据?
- 比特币的私钥【区块链生存训练】
- Unity3D学习笔记第一课
- Extjs4处理后台json数据中日期和时间的方法
- 机器学习:Python测试线性可分性的方法
- Java 机器学习库Smile实战(一)SVM
- 交易Transaction【区块链生存训练】
- 马尔可夫链文本生成的简单应用:不足20行的Python代码生成鸡汤文
- 最长递增子序列
- dedecms批量删除文档关键词可以吗
- 【学术】在C ++中使用TensorFlow训练深度神经网络
- 一个canonical标签解决site不在首页的问题
- 由一道面试题来了解进程间的通信
- 【教程】简单教程:用Python解决简单的水果分类问题
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 如何绕过堡垒机远程登录
- Android | okhttp细枝篇
- valgrind测试报告分析
- 深度学习Pytorch检测实战 - Notes - 第5章 单阶多层检测器:SSD
- Java+selnium 智能等待,try catch方法智能定位需添加等待的元素
- Python中的命名空间和作用域(2)
- C/C++可以用正则表达式吗?
- typescript实战总结之实现一个互联网黑白墙
- 文件上传漏洞演示(一句话木马文件 + 蚁剑)
- [Bazel]构建Golang项目
- 2020--IDEA破解失败后无法打开(mac/win)【已解决】
- CPU:别再拿我当搬砖工!
- 7类 登录/注册 安全漏洞
- 安全弹出你的移动设备,保护数据安全!
- burpsuite系列