并发包相对于Synchronize的优势
并发包相对于synchronize而言,
功能性上:提供了获取与释放锁的可操作性。支持非阻塞式、可中断以及超时获取锁的机制。
性能上:使用volatile与CAS是种乐观锁的思想,避免直接使用重量级的锁,避免了多个线程被迫串行以及上下文切换过程,提高了并发性能。
缺陷:只适用于单机版本,无法适用于多机服务。
AQS理解
并发包提供的各种锁比如可重入锁、读写锁等的底层是通过AQS实现的。
AQS解决了实现锁时涉及当的大量细节问题,例如获取锁的同步状态(通过volatile变量 + CAS操作实现)、维护了与synchronize对等的两个队列(锁池队列以及等待队列(多个等待队列)),并且支持公平锁与非公平锁的机制。AQS底层是volatile以及CAS,这里处理了CAS操作的大量细节,比如队列状态维护,线程休眠时间控制等。
基于AQS来构建同步器可以带来很多好处。它不仅能够极大地减少实现工作,而且也不必处理在多个位置上发生的竞争问题。
AQS具体介绍:《并发编程的艺术》p121
AQS实现自定义锁的要点
实现的核心点:
- get、set、compareAndSetState接口获取以及设置锁的同步状态Volatile state变量。
- 提供tryAcquire、tryRelease、tryAcquireShared、tryReleaseShared等接口,具体实现独占锁以及共享锁
提供acquire、acquired等模板方法,这些模板方法分为三类:独占式获取与释放、共享式获取与释放、查询同步队列情况等方法。方便直接调用
这里就是一种模板模式,abstract抽象类,提供了抽象方法让用户去自己实现从而定义自己的锁。
并发包同步队列底层总结
同步队列的核心点:
head、tail是volatile保持可见性与有序性,通过compareAndSetTail与casHead保证操作的原子性
添加节点的时候是自旋操作,不涉及任何的线程休眠
在AQS提供的acquire方法中,
添加到同步队列之后,node等待成为头结点的后继节点之后,尝试casState,一旦成功即为获取到锁,此时将自己更新为头结点
如果非头节点,则会使用lockSupport让当前线程休眠,退出时间片的竞争。
在release方法中,会去唤醒同步队列head节点的一个节点,唤醒休眠态的节点。
这里自旋会首先判断自己是否是头结点,原因:维持FIFO、防止伪唤醒迅速判定状态然后继续休眠。
公平与否的实现就是看是否要做头结点的判断,如果不公平,则是直接去tryAcquire。
关于共享锁的实现,与独占锁之间的区别就在于volatile state的不同,独占锁是0,1,共享锁是比1大的数字,从而支持重入,支持读写分离。
在state为0的时候意味着没有任何线程获取到锁,非0的时候意味着有线程获取到锁,然后进行后续判断与处理即可,比如可重入的话就判断线程是否相同。
读写锁则是利用了int的高低位之间的区别,从而完成读写状态的判断。
并发包对wait、notify机制的优化:
之前的object的wait-notify机制,只支持一个object.wait
lock可以设置多个等待队列,new 多个condition即可。
阻塞队列:
常用于生产者消费者模式
参考
AQS介绍:
https://juejin.im/entry/5ae02a7c6fb9a07ac76e7b70
LinkedBlockingQueue源码分析与api介绍:
https://blog.csdn.net/tonywu1992/article/details/83419448
https://blog.csdn.net/weixin_41771218/article/details/83056052