AQS: Java 并发基础框架

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 的工作流程可以大致分为以下几个步骤:

  1. 获取资源:调用 acquire 或 acquireShared 尝试获取资源。

    • 如果资源可用,则更新状态字段 state 并退出。
    • 如果资源不可用,将当前线程包装成 Node 节点并加入等待队列。
  2. 进入队列:线程被插入到 FIFO 等待队列中等待唤醒。

    • 使用 ReentrantLock 之类的机制来保证队列的线程安全性,防止多个线程同时操作队列。
  3. 释放资源:调用 release 或 releaseShared 释放资源。

    • 更新状态字段 state
    • 唤醒等待队列中的后继线程,使其尝试再次获取资源。
  4. 唤醒等待线程:当有线程释放资源时,等待队列中的线程依次被唤醒,并尝试重新获取资源。

代码示例

以下是一个基于 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 等待队列机制,实现了高效的线程同步。通过理解这些同步工具的具体逻辑,可以更好地设计和优化基于并发的应用程序。

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值