AQS并发框架介绍
AQS并发框架介绍
一、jdk高级并发组件介绍
1、CyclicBarrier
协调多线程阻塞在某个屏障点,并在指定数量线程全达到屏障点后同时解除阻塞。(面包车的例子)
可以循环使用
使用lock.condition实现
如何使用:
Await 当我们拿到1个CyclicBarrier实例时,如果调用await(),它的线程到达这个障碍点。CyclicBarrier(int parties) 指定达到的数量,线程过来调用await()方法,直到线程数量达到parties,解除阻塞。
1、CountdownLatch
使用场景:使线程阻塞直到指定数量的操作完成。(红绿灯的例子)
使用AQS实现
不可循环使用
CountdownLatch使用:
CountdownLatch(int count);过来一个线程调用await()方法;
countDown() 倒数,减一逐渐减为0,减为0后,之前调用await()的线程全部解除阻塞。CountdownLatch和线程数没有关系
如果countDown()变为0后,后续的线程如果再调用await方法直接通过。这也是和CyclicBarrier的不同点。
1、总结:CountdownLatch和CyclicBarrier有什么不同?
①CountdownLatch不可循环使用
②CountdownLatch:任意多个线程阻塞直到其它线程countDown()为0为止;CyclicBarrier,线程数量达到指定数量解除阻塞
③CyclicBarrier使用lock.condition实现,CountdownLatch使用AQS实现。
利用CountdownLatch和CyclicBarrier模拟高并发
1、Semaphore
使用场景:控制并发资源数量(停车场例子)
不可重入
支持公平和非公平
Sempahore如果传1就类似于lock。
信号量为1的Sempahore和lock的区别:
①Sempahore不可重入;
②要求拥有锁的人进行释放,而Sempahore不要求持有线程才释放
API:
① acquire() 获得锁
② Release()释放锁 构造函数Sempahore(int permits)
1、ReentrantLock
使用场景:严格互斥,同一时间只允许一个线程访问,只有当前线程允许释放。
支持公平和非公平
一、AbstractQueuedSynchronizer(AQS)
1、并发组件的共性
① 阻塞 和 唤醒
② 阻塞和唤醒的逻辑
③ 公平和非公平:阻塞时可能需要排队
2、基于AQS实现的并发类
Semaphore
CountdownLatch
ReentratnLock
ReentratnReadWriteLock
worker
3、AQS基石
1)阻塞和唤醒:LockSupport.park unpark
• 底层使用pthread_cond_timedwait & pthread_cond_signal
2)Permit logic : int state
并发中的if then问题
3)Atomic update :Unsafe.compareAndSwapXX() (CAS)
底层使用 cmpxchg
4) Fair & unFair: CLH queue
5) CAS
API:compareAndSetXX(field,expect,new)
自旋乐观锁,尝试一下,如果不成再试一次。
Linux底层实现:
Lock cmpxchg
注意,cmpxchg本身非原子性,多核需要加lock辅助
6)LockSupport.park 和await的区别
park是不含任何业务逻辑,是无条件的,await()等待,要求别人通知我,蕴涵了业务逻辑的概念。
4、AQS 的CLH QUEUE
一个双线链表(不用考虑扩容,性能高)
Head不含重要语义,其它队列里存的是等待的线程。
在没有高级同步API帮助下,如何保证同步安全操作?
①For +CAS
②每个节点对应一个线程
③WaitStatus:该节点等待状态,用CAS修改。
1)0
2)signal:后一个node将会block,当前节点relase/cancel时,需要unpark后一个节点。(需要把信息给别人)
3)CANCELLED:timeout/interrupt
4)CONDITION:
5)PROPAGATE:在share模式下使用,因为可能需要release多个后续节点
Dummy header - header 对应的节点为非阻塞
1、AQS 的API
1)AQS使用模板模式
acquire
release
acquireShared
releaseShared
2)子类需要实现的API(根据shared/exclusive选其一)
tryAcquire
tryRelease
tryAcquireShared
tryReleaseShared
2、AQS核心API
• acquire: tryAcquire(), 如果失败则入queue, 自旋park,成功则更新
head。
• release: tryRelease(), 如果成功(一般都成功), unpark后继节点 。
• acquireShared: tryAcquireShared() , 如果失败入queue,自旋park。
• releaseShared:如果成功(一般都成功), 级联unpark后继节点。
• 所以exclusive 和 shared模式最大的不同就是shared模式下,可能需要unpark后续n个节点而exclusive只用一个。
3、AcquireShared
首先入队,一直处于循环状态,
1、AQS template pattern
创建1个AQS子类,一般起名为sync
根据是shared还是exclusive选择实现sync中对应API
根据业务逻辑选择当前并发工具类的API对应的sync的API
2、ReentrantLock实现
1)State :
0 无锁
1 有锁
2)可重入控制
exclusiveOwnerThread
3)公平和非公平锁
公平
非公平:只体现在第一次上面
4)Condition
condition使用另一个 queue:condition queue
Await:将节点加入到condition queue 并park
Signal/signalAll:将节点从condition queue 移动到 sys Queue
await() 把锁放掉
问题:下面这段代码,调用singal()时,T1会不会执行wait()下面的方法?
不会,因为singal(),只是把队列放到了 sys Queue里面,只有执行了unlock()方法,当T1获得锁后,开始执行。
调用condition.wait() 和。Signal()之前,为什么要加lock()
If then时,容易发生并发,其它线程修改其状态,导致永远不能唤醒
总结:统一理解
AQS 使用clh队列支持公平性
AQS 使用park/unpark来阻塞/唤醒线程
AQS 使用state字段管理阻塞逻辑