详解JUC之锁——Lock与AQS(02)

本文深入探讨了Java并发工具包(JUC)中的锁机制,包括Lock接口与AbstractQueuedSynchronizer(AQS)类的基本概念及作用。文章详细介绍了Lock接口提供的主要方法,并解释了AQS作为多种锁实现的基础框架的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

详解JUC之锁——概述(01)中我对JUC中的锁进行了概述,下面我就介绍一下它们的根基Lock接口和AQS

Lock

看名字就知道Lock接口就是JUC中锁的顶级接口,支持语义不同的锁规则,比如说公平锁和非公平锁,独占锁(也可以叫互斥锁)和共享锁等。

它最主要的两个方法就是lock()unlock(),一看就知道是获取锁和释放锁。

还有一个比较有趣的方法是boolean tryLock(long time, TimeUnit unit)方法,这个方法没有lock()方法那么死心眼,它在尝试获取锁,获取到了就返回true,一段时间获取不到就算了,返回一个false

当然了,还有Condition newCondition()方法,返回与之关联的Condition对象

AbstractQueuedSynchronizer

AbstractQueuedSynchronizer就是大名鼎鼎的AQS类,怎么说呢,这个类就是JUC锁的根基,是JUC中不同锁的共同抽象类,锁的许多公共方法都是在这个类中实现的,我给你看看它类继承结构你就知道了

有没有发现,无论是带有Lock后缀的类如ReentrantLock还是不带Lock后缀的类如Semaphore它们里面的内部类Sync都继承了AQS。其实JUC中的各种锁只是一个表面装饰,它们里面真正实现功能的还是Sync

我再来给你看一个比根基更根基的东西,那就是AQS类的一个成员变量state

/**
 * The synchronization state.
 */
private volatile int state;

看注释说这个就是同步状态,可能到现在你还没有概念。那我问你,锁最主要的操作是什么?不就是获取锁释放锁嘛!!上面我讲了JUC中不同锁的很多公共方法是在AQS类实现的,其中就有获取锁和释放锁的方法,而我可以大声地告诉你,JUC中所有锁的获取锁和释放锁操作都是围绕着这个同步状态state进行加减操作。每次一个线程获取到锁即对应着这个state加1,释放锁就对应着这个state减1(Semaphore就特殊点,它能加n和减n),state为0的时候代表锁空闲。

公平锁和非公平锁

从上面我给出的AQS的继承结构图你会发现,很多锁的名叫Sync的内部类继承了AQS类,而这个Sync又分别有FairSyncNonfairSync类继承它,看名字就知道它们分别是公平锁和非公平锁了。

那公不公平的准则又是什么呢?

说到这个得先介绍一下CLH队列,CLH队列是AQS中“等待锁”的线程队列,比如说独占锁被一个线程占用着,其它线程就必须等待咯,这些线程就是在CLH队列等待的。这个队列的是用链表实现的,你可以看到AQS类有个内部类Node,这个就是链表的节点。CLH是通过自旋+CAS保证节点插入和移除的原子性(这个我在介绍原子类的时候说过类似的),就是说往里面插入或移除一个节点的时候,在并发条件下不会有问题。

至于CLH是什么意思,在Node的注释上有"CLH" (Craig, Landin, and Hagersten) lock queue,目测是三个人的名字缩写,应该是造出它的人吧。

那现在就可以回答公平的准则了。所谓公平,就是大家排队,是按照CLH队列先来先得的规则,即使锁没被任何线程持有,只要一个线程不是处于队头,它也会乖乖地等,公平地获取锁;非公平,那就是插队咯,当线程要获取锁时,它会无视CLH等待队列而直接获取锁,如果锁没有被任何线程持有,那不管它在CLH的那个角落,它都直接获取锁,没什么道德可言。

AbstractQueuedLongSynchronizer和AbstractOwnableSynchronizer

至于AbstractQueuedLongSynchronizer类跟AbstractQueuedSynchronizer类差不多,只不过它里面的statelong类型的,而AbstractQueuedSynchronizer的是int类型的,而且它们都继承了AbstractOwnableSynchronizer类——一个记录当前持有独占锁的线程的抽象类,这个类很简单,维护一个Thread类型变量,这个应该就是记录当前持有独占锁的线程,然后提供它的gettersetter方法,它虽然是一个抽象类,但是里面并没有抽象方法。

<think>嗯,用户问的是AQS,可能在准备面试,需要详细但清晰的解释。首先,AQS是Java并发里的核心,得先介绍它的全称和作用。然后得说明它如何通过一个int变量表示状态,以及FIFO队列管理线程。 接下来,要区分独占和共享,比如ReentrantLock和Semaphore的例子。这里需要提到模板方法模式,用户可能知道设计模式,但得简单解释一下。 然后,实现自定义的步骤,用户可能想知道具体怎么用AQS。需要提到重写tryAcquire和tryRelease,状态变量的使用,比如CAS操作。可能还要举一个简单的互斥例子,这样更直观。 最后,总结AQS的重要性,以及在Java中的应用,比如CountDownLatch、CyclicBarrier这些类。要确保回答结构清晰,分点说明,但不要太技术化,毕竟用户可能正在复习面试,需要易懂的解释。同时要注意数学公式的格式,如果有的话,但这里可能用不上,不过还是得按照规则处理。要检查是否有错别字,语法是否正确,确保回答准确可靠。</think>好的,我来为您系统化梳理AQS的核心要点,采用分步讲解的方式帮助您掌握面试重点: # AQS详解(AbstractQueuedSynchronizer) ## 一、基础认知 1. **定位**:Java并发包(`java.util.concurrent.locks`)的核心同步框架 2. **设计目标**:通过模板方法模式为构建和同步器提供基础框架 3. **核心组成**: - **同步状态**:`volatile int state`(通过CAS操作保证原子性) - **CLH队列**:FIFO双向队列管理等待线程 - **模板方法**:定义了`acquire()/release()`等核心方法,具体实现交由子类 ## 二、核心机制 ### 1. 独占模式(Exclusive) - **典型实现**:ReentrantLock - **特征**: - 同一时刻只允许一个线程获取资源 - 通过`tryAcquire()/tryRelease()`实现 - 支持可重入性设计 ### 2. 共享模式(Shared) - **典型实现**:Semaphore、CountDownLatch - **特征**: - 允许多个线程同时获取资源 - 通过`tryAcquireShared()/tryReleaseShared()`实现 - 传播机制保障唤醒效率 ## 三、关键源码解析 ### 1. 节点状态(Node.waitStatus) ```java static final class Node { // 状态值定义: static final int CANCELLED = 1; // 线程已取消 static final int SIGNAL = -1; // 后继节点需要唤醒 static final int CONDITION = -2; // 处于条件队列 static final int PROPAGATE = -3; // 共享模式传播 } ``` ### 2. 获取流程(acquire()) ```java public final void acquire(int arg) { if (!tryAcquire(arg) && // 步骤1:尝试快速获取 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 步骤2:加入队列并自旋 selfInterrupt(); } ``` ## 四、面试高频问题 ### Q1:AQS如何实现公平/公平? - **公平**:直接尝试获取(可能插队) - **公平**:必须检查队列是否有等待线程 ### Q2:state变量的作用? $$ state = \begin{cases} 0 & \text{未被持有} \\ n & \text{重入次数(可重入)} \\ 许可证数量 & \text{(Semaphore)} \end{cases} $$ ### Q3:为什么使用CLH队列? - **优势**:相比传统同步器更高效地处理: - 线程阻塞唤醒 - 超时控制 - 取消机制 ## 五、实战应用 ### 实现简单互斥: ```java class Mutex implements Lock { private static class Sync extends AbstractQueuedSynchronizer { protected boolean tryAcquire(int acquires) { return compareAndSetState(0, 1); } protected boolean tryRelease(int releases) { setState(0); return true; } } private final Sync sync = new Sync(); public void lock() { sync.acquire(1); } public void unlock() { sync.release(1); } // 其他方法实现... } ``` ## 六、延伸思考 - **synchronized对比**: - 扩展性更好 - 提供更细粒度的控制 - 支持超时、中断等特性 建议结合源码(特别是`AbstractQueuedSynchronizer`类)和JUC工具类的实现进行深入学习,理解设计模式在并发框架中的应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值