AbstractQueuedSynchronizer (AQS) 是 Java 并发包(java.util.concurrent)中的一个基础框架,用于构建线程同步工具如锁、信号量、屏障等。AQS 提供了一种基于 FIFO 队列的等待机制,可以处理线程的阻塞和唤醒,帮助开发者实现各种复杂的同步器。了解 AQS 的设计和工作原理有助于我们深入理解 Java 并发编程。
主要特性和概念
1. 独占模式(Exclusive Mode)
在独占模式下,资源只能被一个线程占有。例如,ReentrantLock 就是基于独占模式实现的。使用独占模式的主要方法有:
acquire(int arg)
:独占模式的获取方法,如果不能立即获取,则进入等待队列。release(int arg)
:独占模式的释放方法,释放资源并唤醒等待队列中的一个线程。
2. 共享模式(Shared Mode)
在共享模式下,资源可以被多个线程同时占有。例如,Semaphore 和 CountDownLatch 就是基于共享模式实现的。使用共享模式的主要方法有:
acquireShared(int arg)
:共享模式的获取方法,如果不能立即获取,则进入等待队列。releaseShared(int arg)
:共享模式的释放方法,释放资源并可能唤醒等待队列中的一个或多个线程。
3. FIFO 等待队列
AQS 通过一个 FIFO 队列管理等待线程,当某个线程无法获取资源时,它会进入这个队列等待,并在资源变得可用时被唤醒。每个节点(Node)代表一个等待的线程。
4. 状态字段
AQS 使用一个整数(state
)字段来表示同步状态。子类通过重写 tryAcquire(int arg)
、tryRelease(int arg)
、tryAcquireShared(int arg)
和 tryReleaseShared(int arg)
等方法来定义具体的同步语义。
整理了这份Java面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记【点击此处】即可免费获取
关键方法
以下是 AQS 中几个核心方法及其作用:
acquire(int arg)
:尝试以独占模式获取资源。如果获取失败则进入等待队列,直到资源可用再次尝试获取。release(int arg)
:以独占模式释放资源,释放成功后将唤醒等待队列中的一个线程。acquireShared(int arg)
:尝试以共享模式获取资源。如果获取失败则进入等待队列,直到资源可用再次尝试获取。releaseShared(int arg)
:以共享模式释放资源,释放后可能唤醒等待队列中的多个线程。tryAcquire(int arg)
:自定义独占模式的资源获取逻辑。tryRelease(int arg)
:自定义独占模式的资源释放逻辑。tryAcquireShared(int arg)
:自定义共享模式的资源获取逻辑。tryReleaseShared(int arg)
:自定义共享模式的资源释放逻辑。isHeldExclusively()
:查询当前资源是否由当前线程独占。
工作原理
AQS 的工作流程可以大致分为以下几个步骤:
-
获取资源:调用
acquire
或acquireShared
尝试获取资源。- 如果资源可用,则更新状态字段
state
并退出。 - 如果资源不可用,将当前线程包装成 Node 节点并加入等待队列。
- 如果资源可用,则更新状态字段
-
进入队列:线程被插入到 FIFO 等待队列中等待唤醒。
- 使用
ReentrantLock
之类的机制来保证队列的线程安全性,防止多个线程同时操作队列。
- 使用
-
释放资源:调用
release
或releaseShared
释放资源。- 更新状态字段
state
。 - 唤醒等待队列中的后继线程,使其尝试再次获取资源。
- 更新状态字段
-
唤醒等待线程:当有线程释放资源时,等待队列中的线程依次被唤醒,并尝试重新获取资源。
代码示例
以下是一个基于 AQS 实现的简单独占锁:
java
代码解读
复制代码
java复制代码 import java.util.concurrent.locks.AbstractQueuedSynchronizer; public class SimpleExclusiveLock { private final Sync sync = new Sync(); private static class Sync extends AbstractQueuedSynchronizer { @Override protected boolean tryAcquire(int arg) { if (compareAndSetState(0, 1)) { 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; } } public void lock() { sync.acquire(1); } public void unlock() { sync.release(1); } public boolean isLocked() { return sync.isHeldExclusively(); } }
小结
Java AQS 是一个强大的框架,通过提供可扩展的同步机制,使开发者能够高效地实现各种同步工具。理解 AQS 的核心概念和工作原理,包括独占模式和共享模式如何通过 FIFO 等待队列管理,状态字段如何影响同步状态,是理解和实现高效并发编程的重要基础。
具体实现工具
Java 并发包(java.util.concurrent)中许多重要的线程同步工具都是基于 AbstractQueuedSynchronizer (AQS) 实现的。以下是一些常见的基于 AQS 实现的线程同步工具及其具体逻辑:
1. ReentrantLock
ReentrantLock 是一种可重入的互斥锁,它有公平锁和非公平锁两种模式。
具体逻辑:
- 独占模式:使用独占模式(Exclusive Mode)获取锁。
- 重入:支持可重入,即同一线程可以多次获得同一把锁,每次加锁计数加一,解锁时计数减一,直到计数为零时释放锁。
- 公平锁:如果设置为公平锁,则等待时间最长的线程优先获取锁。
- 非公平锁:如果设置为非公平锁,则当前线程会尝试立即获取锁,如果失败则进入等待队列。
核心代码示例:
java
代码解读
复制代码
java复制代码 public class ReentrantLock { private final Sync sync; abstract static class Sync extends AbstractQueuedSynchronizer { abstract void lock(); @Override protected boolean isHeldExclusively() { return getState() != 0 && getExclusiveOwnerThread() == Thread.currentThread(); } @Override protected boolean tryRelease(int releases) { if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); int c = getState() - releases; if (c == 0) { setExclusiveOwnerThread(null); setState(c); return true; } setState(c); return false; } } static final class NonfairSync extends Sync { @Override void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } @Override protected boolean tryAcquire(int acquires) { return // 非公平锁的获取逻辑 } } static final class FairSync extends Sync { @Override void lock() { acquire(1); } @Override protected boolean tryAcquire(int acquires) { return // 公平锁的获取逻辑 } } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } public void lock() { sync.lock(); } public void unlock() { sync.release(1); } }
2. ReentrantReadWriteLock
ReentrantReadWriteLock 是一种读写锁,它允许多个读操作并发进行,但写操作是互斥的,并且读写操作之间也是互斥的。
具体逻辑:
- 共享模式:用来实现读锁,多个读线程可以同时获取读锁。
- 独占模式:用来实现写锁,写锁是互斥的,只有一个线程可以获取写锁。
- 读写互斥:当一个线程持有写锁时,其他线程不能获取读锁或写锁。
核心代码示例:
java
代码解读
复制代码
java复制代码 public class ReentrantReadWriteLock { private final ReadLock readerLock; private final WriteLock writerLock; private final Sync sync; abstract static class Sync extends AbstractQueuedSynchronizer { // Read lock and write lock logic // 共享模式获取 protected int tryAcquireShared(int acquires) { return // 读锁获取逻辑 } // 共享模式释放 protected boolean tryReleaseShared(int releases) { return // 读锁释放逻辑 } // 独占模式获取 protected boolean tryAcquire(int acquires) { return // 写锁获取逻辑 } // 独占模式释放 protected boolean tryRelease(int releases) { return // 写锁释放逻辑 } } static final class FairSync extends Sync { // 公平策略的同步器实现 } static final class NonfairSync extends Sync { // 非公平策略的同步器实现 } public static class ReadLock { public void lock() { sync.acquireShared(1); } public void unlock() { sync.releaseShared(1); } } public static class WriteLock { public void lock() { sync.acquire(1); } public void unlock() { sync.release(1); } } public ReentrantReadWriteLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); readerLock = new ReadLock(); writerLock = new WriteLock(); } public ReadLock readLock() { return readerLock; } public WriteLock writeLock() { return writerLock; } }
3. CountDownLatch
CountDownLatch 是一个同步工具类,用于多个线程等待某些事件的完成。
具体逻辑:
- 共享模式:用于实现等待器,调用
await
的线程将等待计数器变为零。 - 计数器:初始计数值由构造函数设置,调用
countDown
会减少计数器,计数器到零时,所有等待的线程会被唤醒。
核心代码示例:
java
代码解读
复制代码
public class CountDownLatch { private final Sync sync; private static final class Sync extends AbstractQueuedSynchronizer { Sync(int count) { setState(count); } @Override protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; } @Override protected boolean tryReleaseShared(int releases) { for (;;) { int c = getState(); if (c == 0) return false; int nextc = c - 1; if (compareAndSetState(c, nextc)) return nextc == 0; } } } public CountDownLatch(int count) { sync = new Sync(count); } public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } public void countDown() { sync.releaseShared(1); } }
4. Semaphore
Semaphore 是一种信号量,用于限制某个资源的并发访问数。
具体逻辑:
- 共享模式:用于实现信号量,调用
acquire
获取一个许可,调用release
释放一个许可。 - 计数器:表示当前可用的许可数量。
核心代码示例:
java
代码解读
复制代码
java复制代码 public class Semaphore { private final Sync sync; abstract static class Sync extends AbstractQueuedSynchronizer { Sync(int permits) { setState(permits); } @Override protected int tryAcquireShared(int acquires) { for (;;) { int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; } } @Override protected boolean tryReleaseShared(int releases) { for (;;) { int current = getState(); int next = current + releases; if (compareAndSetState(current, next)) return true; } } } public Semaphore(int permits) { sync = new NonfairSync(permits); } public void acquire() throws InterruptedException { sync.acquireSharedInterruptibly(1); } public void release() { sync.releaseShared(1); } }
5. CyclicBarrier
CyclicBarrier 是一种同步工具,用于使一组线程相互等待,直到所有线程都到达某个屏障点。
具体逻辑:
- 独占模式:用于实现屏障,在所有线程到达屏障点之前,调用
await
的线程将被阻塞。 - 计数器:计数器到零时,所有等待的线程会被唤醒。
核心代码示例:
java
代码解读
复制代码
java复制代码 public class CyclicBarrier { private final Sync sync; private static class Generation { boolean broken = false; } private final class Sync extends AbstractQueuedSynchronizer { private Generation generation = new Generation(); Sync(int parties) { setState(parties); } int parties() { return getState(); } int arriveAndAwaitAdvance() { for (;;) { int c = getState(); if (c == 0) return 0; int nextc = c - 1; if (compareAndSetState(c, nextc)) { if (nextc == 0) { nextGeneration(); return 0; } else { for (;;) { try { wait(); break; } catch (InterruptedException e) { notifyAll(); } } return nextc; } } } } private void nextGeneration() { notifyAll(); generation = new Generation(); } } public CyclicBarrier(int parties) { if (parties <= 0) throw new IllegalArgumentException(); sync = new Sync(parties); } public int await() throws InterruptedException, BrokenBarrierException { return sync.arriveAndAwaitAdvance(); } }
总结
基于 AbstractQueuedSynchronizer (AQS) 的这些同步工具通过灵活使用独占(Exclusive)和共享(Shared)两种模式,以及 FIFO 等待队列机制,实现了高效的线程同步。通过理解这些同步工具的具体逻辑,可以更好地设计和优化基于并发的应用程序。