IT评测·应用市场-qidao123.com技术社区

标题: 深入理解 Java AQS 原理与 ReentrantLock 实现 [打印本页]

作者: 惊落一身雪    时间: 2025-3-25 12:32
标题: 深入理解 Java AQS 原理与 ReentrantLock 实现
目录

一、AQS 简介

AbstractQueuedSynchronizer(简称 AQS)是 Java 并发包(java.util.concurrent)中最核心的底子组件之一,它为 Java 中的大多数同步类(如 ReentrantLock、Semaphore、CountDownLatch 等)提供了一个通用的框架。理解 AQS 的工作原理对于深入掌握 Java 并发编程至关重要。
AQS 的作用是解决同步器的实现问题,它将复杂的同步器实现分解为简单的框架方法,开发者只需要实现少量特定的方法就能快速构建出可靠的同步器。
二、AQS 核心设计

2.1 核心组成部分

AQS 重要由以下部分组成:
2.2 AQS 的工作原理

AQS 通过模板方法模式,将一些通用的同步操作封装在框架内部,而将特定同步器的特性(如资源是否可获取的判断)交给子类去实现。AQS 提供以下基本操作:
2.3 AQS 的关键方法

AQS 定义了一组需要子类实现的方法:
三、ReentrantLock 与 AQS 的关系

ReentrantLock 是基于 AQS 实现的可重入锁,它通过内部类 Sync(继承自 AQS)来实现锁的基本功能,并通过 FairSync 和 NonfairSync 两个子类分别实现公平锁和非公平锁。
3.1 ReentrantLock 的布局
  1. public class ReentrantLock implements Lock {
  2.     private final Sync sync;
  3.     abstract static class Sync extends AbstractQueuedSynchronizer {
  4.         // 实现锁的基本操作
  5.     }
  6.     // 公平锁实现
  7.     static final class FairSync extends Sync { ... }
  8.     // 非公平锁实现
  9.     static final class NonfairSync extends Sync { ... }
  10. }
复制代码
3.2 ReentrantLock 如何使用 AQS 的 state

ReentrantLock 使用 AQS 的 state 字段来表示锁的持有次数:
四、AQS 关键流程分析

4.1 独占锁的获取流程

当线程调用 ReentrantLock.lock()方法时,实际上会执行以下流程:
  1. public final void acquire(int arg) {
  2.     if (!tryAcquire(arg) &&
  3.         acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
  4.         selfInterrupt();
  5. }
复制代码
  1. private Node addWaiter(Node mode) {
  2.     Node node = new Node(Thread.currentThread(), mode);
  3.     // 尝试快速添加到队列尾部
  4.     Node pred = tail;
  5.     if (pred != null) {
  6.         node.prev = pred;
  7.         if (compareAndSetTail(pred, node)) {
  8.             pred.next = node;
  9.             return node;
  10.         }
  11.     }
  12.     // 快速添加失败,进入完整的入队方法
  13.     enq(node);
  14.     return node;
  15. }
复制代码
  1. final boolean acquireQueued(final Node node, int arg) {
  2.     boolean failed = true;
  3.     try {
  4.         boolean interrupted = false;
  5.         for (;;) {
  6.             // 获取前驱节点
  7.             final Node p = node.predecessor();
  8.             // 如果前驱是头节点,说明轮到当前节点尝试获取锁
  9.             if (p == head && tryAcquire(arg)) {
  10.                 // 获取成功,把当前节点设为头节点
  11.                 setHead(node);
  12.                 p.next = null; // help GC
  13.                 failed = false;
  14.                 return interrupted;
  15.             }
  16.             // 判断是否应该阻塞当前线程
  17.             if (shouldParkAfterFailedAcquire(p, node) &&
  18.                 parkAndCheckInterrupt())
  19.                 interrupted = true;
  20.         }
  21.     } finally {
  22.         if (failed)
  23.             cancelAcquire(node);
  24.     }
  25. }
复制代码
4.2 独占锁的释放流程

当线程调用 ReentrantLock.unlock()方法时,会执行以下流程:
  1. public final boolean release(int arg) {
  2.     if (tryRelease(arg)) {
  3.         Node h = head;
  4.         if (h != null && h.waitStatus != 0)
  5.             unparkSuccessor(h);
  6.         return true;
  7.     }
  8.     return false;
  9. }
复制代码
[code]private void unparkSuccessor(Node node) {    // 获取当前节点的等待状态    int ws = node.waitStatus;    if (ws < 0)        compareAndSetWaitStatus(node, ws, 0);    // 找到下一个需要唤醒的节点    Node s = node.next;    if (s == null || s.waitStatus > 0) {        s = null;        // 从尾部向前查找需要唤醒的节点        for (Node t = tail; t != null && t != node; t = t.prev)            if (t.waitStatus




欢迎光临 IT评测·应用市场-qidao123.com技术社区 (https://dis.qidao123.com/) Powered by Discuz! X3.4