JAVA中AQS锁原理解析
AQS(AbstractQueuedSynchronizer)是Java中用于实现锁和同步器的基础框架。本文将对AQS的原理进行详细解析,包括其内部数据结构、实现方式以及代码示例。
AQS简介
AQS是一个抽象类,提供了一种实现同步器的简单框架。它定义了两种类型的同步器:独占模式和共享模式。独占模式同步器在同一时刻只能有一个线程获取锁,而共享模式同步器可以允许多个线程同时获取锁。
内部数据结构
AQS内部维护了一个FIFO队列,用于存储等待获取锁的线程。这个队列采用了一个双向链表的数据结构,每个节点表示一个等待获取锁的线程。另外,AQS还维护了一个状态变量,用于标识锁的状态。
实现方式
AQS通过一个volatile类型的int变量来表示锁的状态。当该变量的值为0时,表示锁是未获取状态;当值大于0时,表示锁已被某个线程获取;当值小于0时,表示锁在等待状态。
独占模式
对于独占模式同步器,AQS主要通过实现acquire和release方法来实现加锁和释放锁的逻辑。
acquire方法:
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
release方法:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
在acquire方法中,首先尝试使用tryAcquire方法来获取锁。如果获取失败,则调用acquireQueued方法将当前线程加入到等待队列中,并且阻塞当前线程。当锁被释放后,会唤醒等待队列中的线程。
在release方法中,首先调用tryRelease方法来释放锁。如果释放成功,则唤醒等待队列中的下一个线程。
共享模式
对于共享模式同步器,AQS的实现方式与独占模式基本相同,只是对于等待队列的操作稍有不同。在共享模式中,等待队列中的线程可以同时获取锁。
acquireShared方法:
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null;
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
在acquireShared方法中,首先调用tryAcquireShared方法来尝试获取锁。如果获取失败,则调用doAcquireShared方法将当前线程加入到等待队列中,并且阻塞当前线程。当锁被释放后,会唤醒等待队列中的线程。
使用示例
下面通过一个简单的示例来说明AQS的使用方法。
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class MyLock extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
if (getState() == 0) {
// 锁未被获取,尝试获取锁
if (compareAndSetState(0, arg)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if (getState() == arg) {
// 锁已被当前线程持有,释放锁
setState(0);
setExclusiveOwnerThread(null);
return true;
}
return false;
}
public void lock() {
acquire(1);
}
public void unlock() {
release(1);
}
}
在上述示例中,我们自定义了一个锁类MyLock
,并继承了AbstractQueuedSynchronizer
。通过重写tryAcquire
和tryRelease
方法,实现了加锁和释放锁的逻辑。其中,lock
方法调用acquire
方法实现加锁,unlock
方法调用release
方法实现释放锁。
使用示例:
public class Main {
private static MyLock lock = new MyLock();
private static int count = 0;
public static void main(String[] args) {
Runnable task = () -> {
lock.lock();
try {
for (int i = 0; i < 10000; i++) {
count++;
}
} finally {
lock.unlock();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("count: " + count);
}
}
总结
总结来说,AQS是Java中一种基于等待/通知机制实现的抽象锁,通过内部的状态变量和等待队列来管理线程的状态和同步操作。在实际应用中,AQS被广泛应用于各种锁的实现,如ReentrantLock和Semaphore等。通过深入理解AQS的原理和机制,我们可以更好地理解和使用Java中的锁机制。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)