AQS知识点整理

AQS是Java并发包JUC的核心,用于构建锁和其他同步组件。它维护一个int状态变量和FIFO队列,通过CAS和线程阻塞实现并发控制。ReentrantLock、CountDownLatch等工具类都基于AQS。公平锁遵循线程调用顺序,而非公平锁则允许线程直接尝试获取锁,提高效率但可能导致饥饿现象。

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

什么是AQS?
即AbstractQueuedSynchronizer,队列同步器,是用来构建锁或者其他同步器组件的重量级基础框架及整个JUC体系的基石,通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int类型的变量表示持有锁的状态;当有线程获取不到锁时,就将线程加入该队列中,通过CAS自旋和LockSupport.part()的方式,维护state变量,达到并发同步的效果。

其中,ReentranLock,CountDownLatch,ReentrantReadWriteLock``,Semaphore底层都是AQS

原理:AQS使用一个violatile的int类型的成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作,将每条要抢占资源的线程封装成一个Node节点来完成锁的分配,通过CAS完成对state值的修改;

AQS的核心思想是什么?

AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中

CLHCraig,Landin andHagersten队列,是一个单向链表,AQS中的队列是CLH扩展的虚拟双向FIFO队列;

公平锁与非公平锁的区别
公平锁:多个线程按照线程调用lock()的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁。

优点:所有的线程都能得到资源,不会饿死在队列中。
缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。
非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。

优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。
缺点:这样可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死。

AQS实现方式:

独占式和共享式

先从ReentrantLock#lock方法开始。
该方法定义如下

/**
 * Acquires the lock.
 *
 * <p>Acquires the lock if it is not held by another thread and returns
 * immediately, setting the lock hold count to one.
 *
 * <p>If the current thread already holds the lock then the hold
 * count is incremented by one and the method returns immediately.
 *
 * <p>If the lock is held by another thread then the
 * current thread becomes disabled for thread scheduling
 * purposes and lies dormant until the lock has been acquired,
 * at which time the lock hold count is set to one.
 */
public void lock() {
    sync.lock();
}

其中sync是AbstractQueuedSynchronizer的实现,我们知道,ReentrantLock支持公平锁和非公平锁,其实现类分别是FairSync和NonfairSync

// FairSync 公平锁的实现
final void lock() {
    acquire(1);
}

// NonfairSync 非公平锁的实现
final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

可以看到,非公平锁的实现仅仅是多了一个步骤:通过CAS的方式(compareAndSetState)尝试改变state的状态,修改成功后设置当前线程以独占的方式获取了锁,修改失败执行的逻辑和公平锁一样。

<think>好的,我现在需要帮用户解答关于AQS的面试题和知识点。首先,用户提供了几个引用内容,我需要仔细看看这些引用里有什么有用的信息。引用1提到了AQS是什么、核心原理、公平锁和非公平锁的区别,以及非公平锁的加锁解锁流程。引用2说线程池中的Worker基于AQS实现。引用3提到ReentrantLock、CountDownLatch、Semaphore等同步组件基于AQS。引用4提到Semaphore、CountDownLatch等的并发工具。引用5是关于双重检查锁和volatile的,可能和AQS的关系不大,但可能涉及并发知识。 用户的问题是想要了解AQS相关的面试题或知识点,特别是解析Java并发的部分。我需要根据这些引用内容,结合自己的知识,整理出常见的AQS面试题,并给出解析。 首先,AQS是什么?根据引用1和3,AQS是AbstractQueuedSynchronizer,是Java并发包的核心框架,用于构建锁和同步器。它内部使用了一个FIFO队列来管理线程的排队工作,比如获取锁失败的线程会被加入队列中。 接下来,核心原理部分。AQS通过一个int类型的state变量来表示同步状态,比如ReentrantLock中state表示重入次数,Semaphore中表示许可数量。引用1提到AQS的核心是state变量和CLH队列,以及模板方法模式,子类需要实现tryAcquire和tryRelease等方法。 然后是公平锁和非公平锁的区别,引用1提到公平锁严格按照FIFO顺序获取锁,而非公平锁允许插队,可能造成饥饿但吞吐量高。非公平锁的流程可能是在尝试获取锁时直接CAS修改state,失败后再进入队列排队。 需要检查是否有其他常见面试题,比如AQS的应用场景,如ReentrantLock、CountDownLatch、Semaphore等,这些在引用3和4有提到。此外,可能还涉及AQS的模板方法,比如acquire、release等,以及如何自定义同步器。 可能还需要提到AQS的两种模式:独占模式和共享模式。独占模式如ReentrantLock,共享模式如Semaphore和CountDownLatch。引用4中的Semaphore和CountDownLatch属于共享模式,允许多个线程同时执行。 在回答时,要确保使用正确的中文,数学表达式用$...$格式,独立公式用$$...$$。但根据用户的问题,可能数学公式较少,但如果有状态变量的说明,可能需要用行内格式,比如state变量用$state$。 另外,用户可能需要知道AQS中的等待队列是CLH变体,使用双向链表。还有条件变量Condition的实现,每个ConditionObject维护一个等待队列。 需要确保引用来源正确,比如引用1到5,在回答末尾添加引用标识。比如在提到AQS是什么时,引用[^1];在提到ReentrantLock等组件时引用[^3];在共享模式时引用[^4]。 可能的问题点:用户提供的引用5是关于单例模式的双重检查锁和volatile,这可能和AQS关系不大,但可能作为并发的其他知识点出现。但用户的问题集中在AQS,所以可能不需要涉及这部分,除非在相关问题中提到。 生成的相关问题可能包括:AQS的工作原理,如何基于AQS实现自定义锁,AQS的共享模式与独占模式的区别,AQS在哪些并发工具中的应用,公平锁和非公平锁的实现差异等。 需要确保回答结构清晰,分点解答,每个问题点给出解析,并正确引用来源。例如,在解释AQS核心原理时,引用[^1];在应用场景时引用[^4]。 总结下来,回答的结构应该是先介绍AQS的定义和作用,然后分点解析常见面试题,如核心原理、公平锁与非公平锁的区别、应用场景等,最后生成相关问题。需要确保每个部分都有对应的引用支持,并且语言通顺,符合用户要求的中文回答,正确使用LaTeX格式。</think>### AQS 面试题解析(Java 并发) #### 1. **AQS 是什么?** AQS(AbstractQueuedSynchronizer)是 Java 并发包的核心框架,用于构建锁和同步器(如 `ReentrantLock`、`Semaphore`)。它通过一个 **FIFO 队列**管理线程的排队等待,并通过 **`state` 变量**(整型)表示同步状态(如锁的持有次数、信号量许可数)[^1]。 --- #### 2. **AQS 的核心原理** - **`state` 变量**: 表示资源状态,如 `ReentrantLock` 中 `state` 记录锁的重入次数,`Semaphore` 中表示可用许可数。通过 **CAS 操作**保证原子性修改。 - **CLH 队列**: 使用双向链表实现的等待队列,存储竞争资源失败的线程。线程通过 `acquire()` 方法尝试获取资源,失败后加入队列并阻塞;释放资源时唤醒后继线程。 - **模板方法模式**: 子类需实现 `tryAcquire()`(独占模式获取资源)、`tryRelease()`(释放资源)等方法,AQS 负责队列管理和线程阻塞/唤醒。 --- #### 3. **公平锁与非公平锁的区别** - **公平锁**: 严格按照 FIFO 顺序分配锁,保证无饥饿,但吞吐量较低。 实现:在 `tryAcquire()` 中检查队列是否有等待线程,若有则直接失败。 - **非公平锁**: 允许线程“插队”(即使队列中有等待线程,新线程仍可尝试直接获取锁),吞吐量高,但可能导致饥饿。 实现:`ReentrantLock` 默认非公平锁,直接通过 CAS 修改 `state` 竞争资源[^1]。 --- #### 4. **AQS 的应用场景** - **独占模式**: `ReentrantLock`(可重入锁)、`ReentrantReadWriteLock`(读写锁)。 - **共享模式**: `Semaphore`(信号量,控制并发线程数)、`CountDownLatch`(倒计时门闩,等待多个任务完成)。 --- #### 5. **AQS 的线程阻塞与唤醒** - **`LockSupport` 工具类**: AQS 使用 `LockSupport.park()` 阻塞线程,`LockSupport.unpark(thread)` 唤醒线程,底层依赖 `Unsafe` 类实现[^2]。 --- #### 6. **基于 AQS 实现自定义锁** 示例代码框架: ```java public class MyLock extends AbstractQueuedSynchronizer { @Override protected boolean tryAcquire(int arg) { if (compareAndSetState(0, 1)) { // CAS 修改 state setExclusiveOwnerThread(Thread.currentThread()); // 设置持有线程 return true; } return false; } @Override protected boolean tryRelease(int arg) { if (getState() == 0) throw new IllegalMonitorStateException(); setExclusiveOwnerThread(null); setState(0); // 释放锁 return true; } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值