【并发专题】AQS 连环炮
本问题对应的项目链接:高频题回答模版训练
说明:一般来说,即时有问AQS,也不会问很难,我们只需要从整体的角度,知道他是如何保证线程安排的就可以了,AQS的底层也是基于CAS的。
1、了解 AQS 吗?AQS 的全名叫什么?(PS:这个问题,面试官还真问过)
2、大致讲解一下 AQS 是怎么实现线程安全的?
3、基于AQS的实现的同步工具,你了解哪些?(PS:简单介绍两三个就可以了)
本问题对应的项目链接:高频题回答模版训练
说明:一般来说,即时有问AQS,也不会问很难,我们只需要从整体的角度,知道他是如何保证线程安排的就可以了,AQS的底层也是基于CAS的。
1、了解 AQS 吗?AQS 的全名叫什么?(PS:这个问题,面试官还真问过)
2、大致讲解一下 AQS 是怎么实现线程安全的?
3、基于AQS的实现的同步工具,你了解哪些?(PS:简单介绍两三个就可以了)
共以下 3 个回答
1、了解 AQS 吗?AQS 的全名叫什么?
AQS是一个用来构建锁或其他同步组件的框架,内部用一个volitale修饰的state变量来表示同步的状态,用一个先入先出的队列来完成线程的排队工作。AQS全称是AbstractQueuedSynchronizer,直译是抽象队列同步器,是一个抽象类,由子类继承该类的抽象方法来管理同步状态。
2、大致讲解一下 AQS 是怎么实现线程安全的?
首先AQS使用一个volitale修饰的state变量来表示同步状态,并通过一个先入先出队列来实现线程排队。对于独占式同步状态的获取和释放,队列中每一个节点就代表一个等待的线程,一个线程请求获取锁,成功则将该线程作为头结点,失败则进入尾节点排队。当当前线程释放锁的时候,会唤醒头结点的后继结点,后继结点获取锁并将自己设置为头结点,以此往复。在此过程中锁的获取是通过CAS机制保证的,因此队列头结点和尾节点设置都是线程安全的。针对共享锁,AQS会设置一个计数器规定最多有多少线程能共享,当有线程进入的时候通过CAS机制将计数器减1。
3、基于AQS的实现的同步工具,你了解哪些?
1、AQS全名叫做AbstractQueuedSynchronizer,抽象队列同步器,它是一个抽象类,提供了一种基于先进先出的等待队列框架,用来实现锁和同步工具,内部有一个int类型的变量state,用来记录锁的状态,用来判断锁是否被占用,同时AQS定义了一些获取和释放锁的方法,AQS的具体功能,都是由它的子类来实现
2、AQS底层使用CAS来保证对锁和队列节点的操作是线程安全的。
对于互斥锁,AQS把当前等待锁的线程和等待状态封装成一个node节点,这个节点会加入到等到队列里面,当线程释放锁的时候,会唤醒队列里面的第一个节点,让它获取锁。
对于共享锁,AQS使用一个计数器来跟踪当前的锁还能被多少个线程获取,当一个线程获取到锁,计数器减一;当一个线程释放锁,计数器加一,当计数器结果为0的时候,就不能再获取锁了,此时新来的线程要进入等待队列,如果有线程释放锁,就唤醒队列里面的线程,针对读写锁,当写锁被释放的时候,会唤醒所有的读线程,但是只唤醒第一个写线程
3、AQS常见的子类有ReentrantLock、CountDownLatch、CycliBarrier、Semaphore。
ReentrantLock可以用来加锁和解锁,支持公平锁和非公平锁,支持响应中断;
CountDownLatch用来让当前线程等待指定的线程数,等这些线程执行完之后,才能往下继续执行;
CycliBarrier是当指定的线程数到达某个位置的时候,才继续往下执行;
Semaphore是指定一个共享变量,同一时间最多只能被多少个线程占有。
1、了解 AQS 吗?AQS 的全名叫什么?(PS:这个问题,面试官还真问过)
AQS 是用来构建锁或者其它同步组件的基础框架,内部用一个 int 变量来表示同步状态,并用 FIFO 队列来完成资源获取线程的排队工作。
它全称为 AbstractQueuedSynchronizer 抽象队列同步器,是一个抽象类,通常由子类继承 AQS 并实现它的抽象方法来管理同步状态。
2、大致讲解一下 AQS 是怎么实现线程安全的?
可以从三个方面来说:底层数据结构,独占式同步状态的获取与释放和共享式同步状态的获取与释放。
首先 AQS 底层使用 volatile int 变量 state 来表示同步状态,通过内部的 FIFO 双向队列来实现多线程的排队工作。
对于独占式同步状态的获取和释放来说,队列的一个节点代表一个线程及其等待状态,若某个线程请求获取同步状态,成功则将其作为队列的头结点,失败则将其添加到队列的尾部并尝试自旋。当头结点的线程释放同步状态时,会同时唤醒头结点的后继节点,后继节点在获取同步状态成功后会将自己设置为头结点,以此类推。需要注意的是,这整个过程需要用 CAS 操作保证同步状态的获取,队列头尾节点的设置都是线程安全的。
共享式获取与独占式获取的区别在于,共享式访问资源时,其它共享式的访问都被允许,而独占式访问将被阻塞。正是由于同时可能有多个线程拥有同步状态,共享式释放同步状态也需要用 CAS 确保线程安全。
3、基于AQS的实现的同步工具,你了解哪些?(PS:简单介绍两三个就可以了)
比如重入锁 ReentrantLock,读写锁 ReetrantReadWriteLock 和等待多线程完成的 CountDownLatch,底层都是基于 AQS 实现的。
ReentrantLock 可以支持一个线程对某个资源的重复加锁,底层是在同一个线程加锁时递增,释放锁时递减同步状态的值。
ReetrantReadWriteLock 内部维护了一对锁,一个读锁和一个写锁。多个线程可以同时获得读锁,并发地进行读取操作。但是,只有一个线程能够获得写锁,同时其他线程获得读锁或写锁将被阻塞,确保了在写操作期间没有其他线程能够读取或写入数据。
CountDownLatch 用于一个或多个线程等待其他线程完成操作。内部维护了一个计数值,任一线程在调用 await( ) 时,如果当前计数值不为 0 则会被阻塞,直到计数值减至 0 为止。而其他线程可以调用countDown( ) 来减小计数值。
4、demo
ReentrantReadWriteLock 示例
CountDownLatch 示例