AQS原理
介绍
AQS(AbstractQueuedSynchronizer)是Java并发编程中的一个核心类,位于 java.util.concurrent.locks
包中。它为构建锁和其他同步器提供了一个框架,许多并发工具类(如 ReentrantLock
、Semaphore
、CountDownLatch
等)都是基于AQS实现的。
AQS的核心思想是通过一个FIFO(先进先出)的等待队列来管理线程的阻塞和唤醒。它使用一个 int
类型的变量来表示同步状态,并通过CAS(Compare-And-Swap)操作来保证状态的原子性更新。
AQS的核心组件
AQS的核心组件包括:
- 同步状态(state):一个
volatile int
类型的变量,用于表示锁的状态。例如,ReentrantLock
使用它来表示锁的持有次数。 - 等待队列:一个FIFO的双向链表,用于存储等待获取锁的线程。
- CAS操作:用于原子性地更新同步状态。
同步状态
同步状态是AQS的核心,它通过 getState()
、setState(int)
和 compareAndSetState(int, int)
方法来操作。以下是一个简单的示例:
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的同步状态来表示锁的持有次数,并通过等待队列来管理等待获取锁的线程。
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的同步状态来表示可用的许可数量,并通过等待队列来管理等待获取许可的线程。
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并发工具类的实现原理,并能够编写高效、安全的并发代码。
附加资源
练习
- 实现一个自定义的
Mutex
类,基于AQS实现一个不可重入的互斥锁。 - 修改
SimpleLock
类,使其支持可重入锁的功能。 - 阅读
ReentrantLock
和Semaphore
的源码,理解它们是如何基于AQS实现的。