谈谈你对AQS的理解

AQS概述

AQS,全称为AbstractQueuedSynchronizer,是Java并发包(java.util.concurrent)中一个核心的框架,主要用于构建阻塞式锁和相关的同步器,也是构建锁或者其他同步组件的基础框架。AQS提供了一种基于FIFO(First-In-First-Out)的CLH(三个人名缩写)双向队列的机制,来实现各种同步器,如ReentrantLock、Semaphore、CountDownLatch等。

AQS与Synchronized的区别

synchronized

AQS

关键字,c++ 语言实现

java 语言实现

悲观锁,自动释放锁

悲观锁,手动开启和关闭

锁竞争激烈都是重量级锁,性能差

锁竞争激烈的情况下,提供了多种解决方案

AQS常见的实现类

  • ReentrantLock 阻塞式锁
  • Semaphore 信号量
  • CountDownLatch 倒计时锁

独占锁与共享锁

AQS根据资源互斥级别提供了两种资源访问模式:独占锁(exclusive)和共享锁(shared)。独占锁一次只允许一个线程持有,如ReentrantLock。共享锁允许多个线程同时访问,如Semaphore和ReadWriteLock的读锁。同时其定义Condition结构提供了wait/signal等待唤醒机制。

原理概述

AQS维护了一个volatile int state变量和一个先进先出的CLH双向队列( 也叫等待队列,其中CLH为这个队列的三个发明者人名的缩写),队列中的节点Node持有线程引用,每个节点均可通过getState()、setState()和compareAndSetState()对state进行修改和访问。

state变量值用来表示锁的状态,0表示无锁,1表示有锁。

Node 节点包含线程的引用和节点的状态信息。

当线程获取锁时,即试图对state变量做修改,如修改成功则获取锁;如修改失败则包装为节点添加到双向队列中,等待持有锁的线程释放锁并唤醒双向队列中的节点。

工作机制

只有一个线程争抢资源

当第一个线程0进入并通过CAS操作成功修改state值(即成功获取了锁或其他同步状态),此时该线程不会被封装成Node节点放到CLH双向队列(等待队列)中。

当第一个线程成功获取锁,此时来了2个线程,分别是线程1和线程2,它们想要获取锁,此时线程0还未释放锁,它们经过CAS操作后都获取锁失败,线程1和线程2就会被封装成Node节点并加入CLH双向队列(等待队列)中进行排队,直到线程0释放锁后被唤醒,被唤醒的线程可以重新尝试获取锁。谈谈你对AQS的理解_第1张图片

如果多个线程共同去抢这个资源是如何保证原子性的呢?

谈谈你对AQS的理解_第2张图片

当多个线程共同去修改state状态的时候,使用CAS自旋锁来保证原子性,确保只能有一个线程修改成功,修改失败的线程将会进入CLH双向队列(等待队列)中进行排队等待

AQS是公平锁吗,还是非公平锁?

  • 新的线程与队列中的线程共同来抢资源,是非公平锁
  • 新的线程到队列中等待,只让队列中的head线程获取锁,是公平锁

排队机制

CLH双向队列(等待队列)中Node节点的排队机制为:当队列为空时则使用尾插法向队列插入一个个使用线程封装好的Node节点,如果队列不为空,就向当前队列的尾部插入节点。总的来说还是使用尾插法插入Node节点

唤醒机制

AQS(AbstractQueuedSynchronizer)的唤醒机制中,唤醒的是CLH双向队列(等待队列)头节点的后继节点(即头节点的next节点),而不是头节点本身。 因为头节点通常是已经成功获取同步状态(如锁)的线程。这个线程正在执行它的临界区代码。

你可能感兴趣的:(juc,java,开发语言,面试,后端)