AbstractQueuedSynchronizer(AQS)队列同步器
手写自旋锁:
public class SpinLock {
private AtomicReference<Thread> cas = new AtomicReference<Thread>();
public void lock() {
Thread current = Thread.currentThread();
// 利用CAS
while (!cas.compareAndSet(null, current)) {
// DO nothing
}
}
public void unlock() {
Thread current = Thread.currentThread();
cas.compareAndSet(current, null);
}
}
自旋锁(spinlock):是指尝试去获取锁的线程不会立即阻塞,而是采用循环的方式去获取锁,这样的好处是减少线程上下文切换消耗,缺点是循环会消耗CPU。
可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响
synchronized和ReentrantLock都是典型的可重入锁。
CAS
:比较和交换(Conmpare And Swap)是用于实现多线程同步的原子指令
- 独占锁、共享锁
- 公平锁、非公平锁、重入锁
- 条件锁
- 读写锁
- 自旋锁
- 偏向锁
-
node.prev = t;
-
if (compareAndSetTail(t, node)) {
-
t.next = node;
-
return t;
该方法就是循环调用CAS,compareAndSetTail 即使有高并发的场景,无限循环将会最终成功把当前线程追加到队尾(或设置队头)。总而言之,addWaiter的目的就是通过CAS把当前线程追加到队尾,并返回包装后的Node实例。
CAS:
4. 关于AQS的问题总结
1. 什么是AQS?与锁的区别和联系?
AQS:AbstractQueuedSynchronizer,队列同步器。
AQS与锁的区别和联系:
AQS是实现锁和其他同步组件的基础框架
AQS是面向锁的实现者,它简化了锁的实现方式,屏蔽了同步状态管理,线程的排队、等待与唤醒等底层操作。
锁是面向使用者,它定义了使用者与锁交互的接口,隐藏了实现细节。
AQS和锁有效的隔离了实现者和使用者所需关注的领域。
2. AQS中,自定义同步器需要重写哪些方法?
基础: AQS中5种指定的可重写方法的名称、作用,tryAcquire()的实现:compareAndSetState()。
进阶: 以实现锁为例,讲解如何使用同步器的实现自定义锁。
3. AQS是什么?底层如何实现?
关于如何为实现:
两个核心(同步状态和同步队列)
更改同步状态的三种核心方法
同步队列的结构、如何设置尾节点(需要CAS保证)和头结点(不需要CAS保证)
————————————————
版权声明:本文为CSDN博主「晓之木初」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
总结下独占式同步状态的获取和释放:在获取同步状态时,同步器维护一个同步队列,获取状态失败的线程都会被加入到队列中并在队列中进行自旋;移出队列的条件是前驱节点为头节点且成功获取了同步状态。在释放同步状态时,同步器调用tryRelease方法释放同步状态,然后唤醒头节点的后继节点。
4、AQS应用
AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。
AQS被大量的应用在了同步工具上。
ReentrantLock:ReentrantLock类使用AQS同步状态来保存锁重复持有的次数。当锁被一个线程获取时,ReentrantLock也会记录下当前获得锁的线程标识,以便检查是否是重复获取,以及当错误的线程试图进行解锁操作时检测是否存在非法状态异常。ReentrantLock也使用了AQS提供的ConditionObject,还向外暴露了其它监控和监测相关的方法。
ReentrantReadWriteLock:ReentrantReadWriteLock类使用AQS同步状态中的16位来保存写锁持有的次数,剩下的16位用来保存读锁的持有次数。WriteLock的构建方式同ReentrantLock。ReadLock则通过使用acquireShared方法来支持同时允许多个读线程。
Semaphore:Semaphore类(信号量)使用AQS同步状态来保存信号量的当前计数。它里面定义的acquireShared方法会减少计数,或当计数为非正值时阻塞线程;tryRelease方法会增加计数,在计数为正值时还要解除线程的阻塞。
CountDownLatch:CountDownLatch类使用AQS同步状态来表示计数。当该计数为0时,所有的acquire操作(对应到CountDownLatch中就是await方法)才能通过。
FutureTask:FutureTask类使用AQS同步状态来表示某个异步计算任务的运行状态(初始化、运行中、被取消和完成)。设置(FutureTask的set方法)或取消(FutureTask的cancel方法)一个FutureTask时会调用AQS的release操作,等待计算结果的线程的阻塞解除是通过AQS的acquire操作实现的。
SynchronousQueues:SynchronousQueues类使用了内部的等待节点,这些节点可以用于协调生产者和消费者。同时,它使用AQS同步状态来控制当某个消费者消费当前一项时,允许一个生产者继续生产,反之亦然。
除了这些j.u.c提供的工具,还可以基于AQS自定义符合自己需求的同步器。
AQS就学习到这,如果有描述不当的地方,还请留言交流。了解了AQS后下一步准备详细学习基于AQS的工具类。