【深入解析AQS】从设计模式到ReentrantLock实现再到自定义锁

深入解析AQS:设计模式、ReentrantLock实现与自定义锁开发

一、模板方法模式:AQS的架构基石

1.1 模式核心思想

模板方法模式通过固定算法骨架+可变实现细节的设计,实现了代码复用与扩展性的平衡。AQS采用这种模式,将同步器的核心流程(如线程排队、阻塞唤醒)固化在父类,仅将资源获取/释放的逻辑通过抽象方法交给子类实现。

设计优势:

  • 保证正确性:关键同步流程不可修改
  • 提高复用:通用逻辑只需实现一次
  • 便于扩展:子类只需关注业务逻辑

1.2 完整代码示例

// 模板类定义 abstract class BeverageMaker {     // 模板方法(final防止重写)     public final void makeBeverage() {         boilWater();         brew();         pourInCup();         if (customerWantsCondiments()) {             addCondiments();         }     }          // 具体方法(通用步骤)     private void boilWater() {         System.out.println("煮沸水");     }          private void pourInCup() {         System.out.println("倒入杯中");     }          // 抽象方法(子类必须实现)     protected abstract void brew();     protected abstract void addCondiments();          // 钩子方法(子类可选覆盖)     protected boolean customerWantsCondiments() {         return true;     } }  // 具体实现类 class TeaMaker extends BeverageMaker {     @Override     protected void brew() {         System.out.println("浸泡茶叶5分钟");     }      @Override     protected void addCondiments() {         System.out.println("加入柠檬片");     }          @Override     protected boolean customerWantsCondiments() {         return false; // 茶不需要调料     } } 

关键点说明:

  1. makeBeverage()定义了不可变的制作流程
  2. brew()addCondiments()是可变部分,由子类实现
  3. customerWantsCondiments()是钩子方法,提供策略扩展点

1.3 AQS中的模板模式实现

AQS将同步操作抽象为模板方法:

public abstract class AbstractQueuedSynchronizer {     // 获取资源的模板方法     public final void acquire(int arg) {         if (!tryAcquire(arg) &&  // 尝试获取(子类实现)             acquireQueued(addWaiter(Node.EXCLUSIVE), arg))             selfInterrupt();     }          // 由子类实现的获取逻辑     protected boolean tryAcquire(int arg) {         throw new UnsupportedOperationException();     }          // 已实现的通用方法     private Node addWaiter(Node mode) {         // 实现节点入队逻辑...     }          final boolean acquireQueued(Node node, int arg) {         // 实现队列中获取资源的逻辑...     } } 

设计精妙之处:

  • acquire()封装了完整的获取资源流程
  • 子类只需关注tryAcquire的核心逻辑
  • 线程排队、阻塞唤醒等复杂操作由AQS统一处理

二、ReentrantLock与AQS的深度整合

2.1 整体架构设计

ReentrantLock通过内部类继承AQS,实现锁的核心功能:

classDiagram class ReentrantLock { -Sync sync +lock() +unlock() } class Sync { <<abstract>> +nonfairTryAcquire() +tryRelease() } class NonfairSync { +lock() +tryAcquire() } class FairSync { +tryAcquire() } ReentrantLock --> Sync Sync <|-- NonfairSync Sync <|-- FairSync Sync --> AQS

2.2 关键源码解析

非公平锁实现:

static final class NonfairSync extends Sync {     // 非公平获取锁     final void lock() {         if (compareAndSetState(0, 1))  // 先尝试快速获取             setExclusiveOwnerThread(Thread.currentThread());         else             acquire(1);  // 进入AQS排队流程     }          protected final boolean tryAcquire(int acquires) {         return nonfairTryAcquire(acquires);     } }  // Sync中的通用非公平尝试 final boolean nonfairTryAcquire(int acquires) {     final Thread current = Thread.currentThread();     int c = getState();     if (c == 0) {  // 锁未被持有         if (compareAndSetState(0, acquires)) {             setExclusiveOwnerThread(current);             return true;         }     }     else if (current == getExclusiveOwnerThread()) {  // 重入逻辑         int nextc = c + acquires;         if (nextc < 0) throw new Error("Maximum lock count exceeded");         setState(nextc);         return true;     }     return false; } 

实现特点:

  1. 快速路径:先直接尝试CAS获取锁,避免排队开销
  2. 重入支持:通过检查当前线程和状态计数实现
  3. 非公平性:新请求线程可能插队获取锁

公平锁实现差异:

static final class FairSync extends Sync {     protected final boolean tryAcquire(int acquires) {         final Thread current = Thread.currentThread();         int c = getState();         if (c == 0) {             if (!hasQueuedPredecessors() &&  // 关键区别:检查队列                 compareAndSetState(0, acquires)) {                 setExclusiveOwnerThread(current);                 return true;             }         }         // 重入逻辑与非公平锁相同...     } } 

公平性保证:

  • hasQueuedPredecessors()检查是否有更早等待的线程
  • 严格按照CLH队列顺序获取锁
  • 吞吐量通常低于非公平锁,但避免线程饥饿

2.3 状态管理机制

AQS使用volatile int state字段记录同步状态,ReentrantLock中表示:

  • 0:锁未被任何线程持有
  • >0:锁被持有,数值表示重入次数

配套原子操作方法:

protected final int getState() {     return state; }  protected final void setState(int newState) {     state = newState; }  protected final boolean compareAndSetState(int expect, int update) {     return unsafe.compareAndSwapInt(this, stateOffset, expect, update); } 

三、基于AQS实现自定义锁

3.1 完整自定义锁实现

import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.AbstractQueuedSynchronizer; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock;  /**  * 基于AQS的简单互斥锁(不可重入)  */ public class SimpleMutexLock implements Lock {     private final Sync sync = new Sync();      // 内部同步器     private static class Sync extends AbstractQueuedSynchronizer {         // 尝试获取锁         @Override         protected boolean tryAcquire(int acquires) {             if (acquires != 1) throw new IllegalArgumentException();             if (compareAndSetState(0, 1)) {  // CAS保证原子性                 setExclusiveOwnerThread(Thread.currentThread());                 return true;             }             return false;         }          // 尝试释放锁         @Override         protected boolean tryRelease(int releases) {             if (releases != 1) throw new IllegalArgumentException();             if (getState() == 0) throw new IllegalMonitorStateException();             setExclusiveOwnerThread(null);             setState(0);  // 不需要CAS,只有持有线程能调用             return true;         }          // 是否被当前线程独占         @Override         protected boolean isHeldExclusively() {             return getState() == 1;         }          // 创建条件变量         Condition newCondition() {             return new ConditionObject();         }     }      // ========== Lock接口实现 ==========     @Override     public void lock() {         sync.acquire(1);     }      @Override     public void lockInterruptibly() throws InterruptedException {         sync.acquireInterruptibly(1);     }      @Override     public boolean tryLock() {         return sync.tryAcquire(1);     }      @Override     public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {         return sync.tryAcquireNanos(1, unit.toNanos(time));     }      @Override     public void unlock() {         sync.release(1);     }      @Override     public Condition newCondition() {         return sync.newCondition();     }      // ========== 扩展方法 ==========     public boolean isLocked() {         return sync.isHeldExclusively();     }          public boolean hasQueuedThreads() {         return sync.hasQueuedThreads();     } } 

实现要点:

  1. 状态定义:0表示未锁定,1表示锁定
  2. 不可重入:不检查当前线程是否已持有锁
  3. 条件变量:直接复用AQS的ConditionObject
  4. 线程安全:CAS操作保证tryAcquire的原子性

3.2 可重入锁改造

修改Sync内部类即可实现可重入:

private static class Sync extends AbstractQueuedSynchronizer {     @Override     protected boolean tryAcquire(int acquires) {         final Thread current = Thread.currentThread();         int c = getState();         if (c == 0) {             if (compareAndSetState(0, acquires)) {                 setExclusiveOwnerThread(current);                 return true;             }         }         else if (current == getExclusiveOwnerThread()) {  // 重入判断             int nextc = c + acquires;             if (nextc < 0) throw new Error("Maximum lock count exceeded");             setState(nextc);             return true;         }         return false;     }      @Override     protected boolean tryRelease(int releases) {         int c = getState() - releases;         if (Thread.currentThread() != getExclusiveOwnerThread())             throw new IllegalMonitorStateException();         boolean free = false;         if (c == 0) {  // 完全释放             free = true;             setExclusiveOwnerThread(null);         }         setState(c);         return free;     } } 

关键修改点:

  1. 增加当前线程检查实现重入
  2. 通过状态计数记录重入次数
  3. 只有计数归零时才完全释放锁

3.3 完整测试用例

public class SimpleMutexLockTest {     public static void main(String[] args) throws InterruptedException {         // 基础功能测试         testBasicOperation();                  // 并发安全测试         testConcurrentAccess();                  // 不可重入性测试         testNonReentrant();     }      private static void testBasicOperation() {         SimpleMutexLock lock = new SimpleMutexLock();         assert !lock.isLocked() : "初始状态应该未锁定";                  lock.lock();         try {             assert lock.isLocked() : "锁定后状态应该为true";             assert !lock.tryLock() : "不可重入锁应获取失败";         } finally {             lock.unlock();         }     }      private static void testConcurrentAccess() throws InterruptedException {         final int THREADS = 5;         final SimpleMutexLock lock = new SimpleMutexLock();         final AtomicInteger counter = new AtomicInteger();         final CountDownLatch startLatch = new CountDownLatch(1);         final CountDownLatch endLatch = new CountDownLatch(THREADS);          ExecutorService executor = Executors.newFixedThreadPool(THREADS);         for (int i = 0; i < THREADS; i++) {             executor.execute(() -> {                 try {                     startLatch.await();                     lock.lock();                     try {                         counter.incrementAndGet();                         Thread.sleep(100);  // 模拟操作                     } finally {                         lock.unlock();                         endLatch.countDown();                     }                 } catch (InterruptedException e) {                     Thread.currentThread().interrupt();                 }             });         }                  startLatch.countDown();         endLatch.await();         assert counter.get() == THREADS : "所有线程应该都执行了";         executor.shutdown();     }      private static void testNonReentrant() {         SimpleMutexLock lock = new SimpleMutexLock();         lock.lock();         try {             // 会阻塞在这里,因为是不可重入锁             boolean acquired = lock.tryLock();              assert !acquired : "不可重入锁不应再次获取成功";         } finally {             lock.unlock();         }     } } 

四、核心知识总结

4.1 模板方法模式在AQS中的应用

  1. 固定流程

    • acquire()/release()定义了标准的获取/释放资源流程
    • 包含线程排队、阻塞唤醒等通用逻辑
  2. 可变部分

    • tryAcquire()/tryRelease()由子类实现
    • 决定如何获取和释放资源
  3. 设计优势

    • 保证同步行为的正确性
    • 提高代码复用率
    • 支持多种同步策略

4.2 ReentrantLock实现要点

特性 非公平锁 公平锁
获取顺序 允许插队 严格FIFO
吞吐量 较低
实现关键 直接尝试CAS 先检查队列
适用场景 大多数情况 避免饥饿

4.3 自定义锁开发原则

  1. 明确状态语义:定义state字段的含义
  2. 保证线程安全:CAS操作保护关键状态
  3. 合理选择特性:根据需求决定是否支持重入、公平性等
  4. 充分测试:验证并发场景下的正确性

结语

通过本文的系统讲解,我们完成了从设计模式理论到AQS框架分析,再到具体锁实现的完整学习路径。理解这些内容后,开发者可以:

  1. 更深入地理解Java并发包的设计思想
  2. 根据特殊需求实现自定义同步器
  3. 更好地选择和使用Java提供的并发工具

建议读者结合文中的代码示例进行实践,通过修改和调试加深对AQS工作机制的理解。对于生产环境,除非有特殊需求,否则应优先使用Java标准库提供的并发工具。

发表评论

评论已关闭。

相关文章