并发专题阶段性总结2
2023-03-20 22:07:51 0 举报
AI智能生成
并发专题
作者其他创作
大纲/内容
作用是控制访问特定资源的线程数目
资源访问,服务限流(Hystrix里限流就有基于信号量方式)。
可以用于做流量控制,特别是公用资源有限的应用场景
使用场景
public Semaphore(int permits)
permits 表示许可线程的数量
如果这个设为 true 的话,下次执行的线程会是等待最久的线程
fair 表示公平性
构造方法
acquire() 表示阻塞并获取许可
public void acquire()
release() 表示释放许可
public void release()
方法在没有许可的情况下会立即返回 false,要获取许可的线程不会阻塞
核心方法
Semaphore信号量
对共享资源有读和写的操作,且写操作没有读操作那么频繁(读多写少
在读多于写的情况下,读写锁能够提供比排它锁更好的并发性和吞吐量
场景
读读可以并发
在没有写操作的时候,多个线程同时读一个资源没有任何问题
允许多个线程同时读取共享资源
没有其他线程的写锁
没有写请求或者有写请求,但调用线程和持有锁的线程是同一个
条件
读锁
读写,写读,写写互
一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写操作了
没有其他线程的读锁
写锁
默认
吞吐量还是非公平优于公平
非公平
公平
公平选择性
可重入
写锁能够降级成为读锁
锁降级
重要特性
读写锁介绍
适合读多写少的场景
一个用于只读操作readLock()
一个用于写入操作writeLock()
写锁是独占的,读锁是共享的
维护了一对相关的锁
默认构造方法为** 非公平模式 ,开发者也可以通过指定fair为true设置为 公平模式 **
public ReentrantReadWriteLock(boolean fair)
读写锁接口ReadWriteLock
独占性和重入性都是通过CAS操作维护AQS内部的state变量实现的
高16位表示当前读锁的占有量
高16位
低16位表示写锁的占有量
低16位
int型
state变量
表示当前读锁未被占有
state的高16位等于0
表示当前读锁可能被一个或多个线程占有,多于一个占有读锁的线程,允许重入。
state的高16位大于0
读锁与写锁互斥
除非当前申请读操作的线程是占有写锁的线程,即实现了写锁降级为读锁
当前的写锁未被占有(AQS state变量低16位为0) 或者当前线程是写锁占有的线程
非公平模式下AQS的锁等待队列head节点后的节点非共享节点(等待读锁的节点),将返回true
在公平模式下只要AQS锁等待队列的头尾不为空,并且存在head后的节点并且节点的线程非当前线程,返回true。
readerShouldBlock()方法返回false
保证读锁的占有数不超过最大上限
当前读锁占有量小于最大值(2^16 -1)
保证多线程竞争读锁时的安全性
成功通过CAS操作将读锁占有量+1(AQS的state高16位同步加1)
获取条件
读锁的释放过程即AQS的state高16位同步递减为0的过程
如果读锁的占有数不为0,表示读锁未完全释放。或者写锁的占有数不为0,表示释放的读锁是写锁降级来的
读锁释放
读锁分析
表示当前写锁没有被占有
反之表示写锁被某个写线程占有(state = 1)或重入(state > 1)
state低16位为0
写锁与读锁互斥,ReentrantReadWriteLock并没有读锁升级的功能
读锁未被占用(AQS state高16位为0) ,写锁未被占用(state低16位为0)或者占用写锁的线程是当前线程
非公平模式下非公平模式下允许满足条件的写操作直接插队
在公平模式下AQS的锁等待队列不为空,写操作无法插队
当前写锁占有量小于最大值(2^16 -1),否则抛出Error(\"Maximum lock count exceeded\")
通过CAS竞争将写锁状态+1(将state低16位同步+1)
写锁的释放过程即AQS的state低16位同步递减为0的过程
如果该写锁占有线程未释放写锁前还占用了读锁,那么写锁释放后该线程就完全转换成了读锁的持有线程
写锁释放
写锁分析
源码分析
ReentrantReadWriteLock
无限队列unbounded queue,几乎可以无限增长
有限队列bounded queue,定义了最大容量
队列类型
数组
链表
队列具备FIFO先进先出的特性,当然也有双端队列(Deque)优先级队列
数据结构
ArrayBlockingQueue 由数组支持的有界队列
LinkedBlockingQueue 由链接节点支持的可选有界队列
PriorityBlockingQueue 由优先级堆支持的无界优先级队列
DelayQueue 由优先级堆支持的、基于时间的调度队列
常见的阻塞队列
添加元素
检索元素
BlockingQueue API
阻塞功能使得生产者和消费者两端的能力得以平衡,当有任何一端速度过快时,阻塞队列便会把过快的速度给降下来
阻塞
容量的大小,分为有界和无界两种
是否有界
阻塞队列特性
介绍
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>();
队列创建
在线程池中有比较多的应用,生产者消费者场景
span style=\
应用场景
基于ReentrantLock保证线程安全,根据Condition实现队列满时的阻塞
一把锁,两个条件
工作原理
初始化时需要指定容量大小,没有扩容机制
没有元素的位置也占用空间被null 占用个
数据结构--数组
出队队列count=0,无元素可取时,阻塞在该对象上
notEmpty
出队队列count=length,放不进去元素时,阻塞在该对象上
notFull
阻塞对象
使用的数组是个环形数组,这样就减少了对数组删除的时间复杂对,增大了率用率和效率
设计精髓
ArrayBlockingQueue
该阻塞队列的大小为Integer.MAX_VALUE
是一个基于链表的无界队列(理论上有界,内存可能会填满,然后就会 OutOfMemory 异常)
blockingQueue 的容量将设置为 Integer.MAX_VALUE
BlockingQueue<String> blockingQueue = new LinkedlockingQueue<>();
取Node节点保证前驱后继不会乱
takeLock
删除元素时两个锁一起加
putLock
存取互不干扰,存取操作是不同的node对象
锁分离
可以指定容量,该阻塞队列的大小为Integer.MAX_VALUE
数据结构--链表
LinkedBlockingQueue实现的队列中的锁是分离的,其添加采用的是putLock,移除采用的则是takeLock,这样能大大提高队列的吞吐量,以此来提高整个队列的并发性能
LinkedBlockingQueue
队列详解
阻塞队列
阻塞等待队列
共享/独占
公平/非公平
允许中断
AQS具备特性
state表示资源的可用状态
getState()
setState()
compareAndSetState()
三种访问方式
AQS内部维护属性volatile int state (32位)
尝试获取资源,成功则返回true,失败则返回false
tryAcquire(int)
尝试释放资源,成功则返回true,失败则返回false
tryRelease(int)
只有一个线程能执行,如ReentrantLock
Exclusive-独占
尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源
tryAcquireShared(int)
尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false
tryReleaseShared(int)
多个线程可以同时执行,如Semaphore/CountDownLatch
Share-共享
AQS定义两种资源共享方式
双向链表数据结构的队列
FIFO先入先出线程等待队列
线程由原自旋机制改为阻塞机制
同步等待队列
Condition是一个多线程间协调通信的工具类
条件等待队列
AQS定义两种队列
特点介绍
需要实现共享资源state的获取与释放方式
具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现
只有用到condition才需要去实现它
isHeldExclusively():该线程是否正在独占资源
tryAcquire(int):独占尝试获取资源
tryRelease(int):独占尝试释放资源
tryAcquireShared(int):共享尝试获取资源
tryReleaseShared(int):共享尝试释放资源
内部定义了一个Sync的内部类,该类继承AbstractQueuedSynchronized,对该抽象类的部分方法做了实现
自定义同步器如何实现
AQS应用
让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行
其参数表示屏障拦截的线程数量
parties
可以用于多线程计算数据,最后合并计算结果的场景
一个Excel保存了用户所有银行流水,每个Sheet保存一个账户近一年的每笔银行流水算出整个Excel的日均银行流水
CyclicBarrier栅栏屏障
能够使一个线程等待其他线程完成各自的工作后再执行
应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行
CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量
每当一个线程完成了自己的任务后,计数器的值就会减1
当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务
原理
CountDownLatch.countDown()
调用 await() 方法的线程会被挂起,它会等待直到 count 值为 0 才继续执行
CountDownLatch.await();
CountDownLatch允许一个或多个线程等待其他线程完成操作,类似于join但比它更灵活
join() 的实现原理是不停检查join线程是否存活,如果 join 线程存活则让当前线程永远等待
CountDownLatch可以手动控制在n或者一个线程里调用n次countDown()方法使计数器进行减一操作
与Thread.join的区别
CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset() 方法重置
CyclicBarrier是通过ReentrantLock的\"独占锁\"和Conditon来实现一组线程的阻塞唤醒的,而CountDownLatch则是通过AQS的“共享锁”实现
与CyclicBarrier的区别
CountDownLatch
并发专题阶段性总结2
0 条评论
回复 删除
下一页