跳到主要内容

AQS原理

介绍

AQS(AbstractQueuedSynchronizer)是Java并发编程中的一个核心类,位于 java.util.concurrent.locks 包中。它为构建锁和其他同步器提供了一个框架,许多并发工具类(如 ReentrantLockSemaphoreCountDownLatch 等)都是基于AQS实现的。

AQS的核心思想是通过一个FIFO(先进先出)的等待队列来管理线程的阻塞和唤醒。它使用一个 int 类型的变量来表示同步状态,并通过CAS(Compare-And-Swap)操作来保证状态的原子性更新。

AQS的核心组件

AQS的核心组件包括:

  1. 同步状态(state):一个 volatile int 类型的变量,用于表示锁的状态。例如,ReentrantLock 使用它来表示锁的持有次数。
  2. 等待队列:一个FIFO的双向链表,用于存储等待获取锁的线程。
  3. CAS操作:用于原子性地更新同步状态。

同步状态

同步状态是AQS的核心,它通过 getState()setState(int)compareAndSetState(int, int) 方法来操作。以下是一个简单的示例:

java
import java.util.concurrent.locks.AbstractQueuedSynchronizer;

public class SimpleLock extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
return compareAndSetState(0, 1); // 尝试将状态从0改为1
}

@Override
protected boolean tryRelease(int arg) {
setState(0); // 释放锁,将状态重置为0
return true;
}

public void lock() {
acquire(1);
}

public void unlock() {
release(1);
}
}

在这个示例中,tryAcquire 方法尝试获取锁,如果当前状态为0(未锁定),则将其设置为1(锁定)。tryRelease 方法则释放锁,将状态重置为0。

等待队列

AQS使用一个双向链表来管理等待获取锁的线程。每个节点(Node)代表一个线程,节点中包含了线程的引用以及一些状态信息。AQS通过 enq(Node)addWaiter(Node) 方法将线程加入等待队列。

CAS操作

CAS(Compare-And-Swap)是AQS实现原子操作的关键。AQS通过 compareAndSetState(int expect, int update) 方法来原子性地更新同步状态。如果当前状态等于 expect,则将其更新为 update,否则不进行任何操作。

AQS的实际应用

AQS广泛应用于Java的并发工具类中。以下是一些常见的应用场景:

ReentrantLock

ReentrantLock 是一个可重入的互斥锁,它基于AQS实现。ReentrantLock 通过AQS的同步状态来表示锁的持有次数,并通过等待队列来管理等待获取锁的线程。

java
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
private static final ReentrantLock lock = new ReentrantLock();

public static void main(String[] args) {
new Thread(() -> {
lock.lock();
try {
System.out.println("Thread 1 acquired the lock");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println("Thread 1 released the lock");
}
}).start();

new Thread(() -> {
lock.lock();
try {
System.out.println("Thread 2 acquired the lock");
} finally {
lock.unlock();
System.out.println("Thread 2 released the lock");
}
}).start();
}
}

Semaphore

Semaphore 是一个计数信号量,它基于AQS实现。Semaphore 通过AQS的同步状态来表示可用的许可数量,并通过等待队列来管理等待获取许可的线程。

java
import java.util.concurrent.Semaphore;

public class SemaphoreExample {
private static final Semaphore semaphore = new Semaphore(2);

public static void main(String[] args) {
for (int i = 0; i < 4; i++) {
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " acquired a permit");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
System.out.println(Thread.currentThread().getName() + " released a permit");
}
}).start();
}
}
}

总结

AQS是Java并发编程中的一个重要组件,它为构建锁和其他同步器提供了一个强大的框架。通过理解AQS的核心机制(同步状态、等待队列和CAS操作),你可以更好地掌握Java并发工具类的实现原理,并能够编写高效、安全的并发代码。

附加资源

练习

  1. 实现一个自定义的 Mutex 类,基于AQS实现一个不可重入的互斥锁。
  2. 修改 SimpleLock 类,使其支持可重入锁的功能。
  3. 阅读 ReentrantLockSemaphore 的源码,理解它们是如何基于AQS实现的。