深入理解 AQS(AbstractQueuedSynchronizer):并发编程的核心

目录

引言

1. 什么是 AQS?

2. AQS 的核心组件

2.1 同步状态(state)

2.2 等待队列

3. AQS 的工作原理

3.1 获取锁(acquire)

3.2 释放锁(release)

4. AQS 的源码实现

4.1 同步状态(state)

4.2 等待队列

4.3 获取锁(acquire)

4.4 释放锁(release)

5. 如何使用 AQS 实现自定义同步器

6. AQS 的应用场景

7. 总结


引言

在 Java 并发编程中,AbstractQueuedSynchronizer(简称 AQS)是一个非常重要的框架。它是许多并发工具类(如 ReentrantLockSemaphoreCountDownLatch 等)的基础。AQS 提供了一种实现阻塞锁和同步器的框架,开发者可以基于 AQS 轻松构建自定义的同步器。

本文将深入探讨 AQS 的核心思想、工作原理、源码实现以及如何使用 AQS 构建自定义同步器。

1. 什么是 AQS?

AbstractQueuedSynchronizer 是 Java 并发包(java.util.concurrent)中的一个抽象类,用于构建锁和其他同步器。它的核心思想是:

  1. 同步状态管理:AQS 通过一个 volatile 的 int 类型变量(state)来表示同步状态。

  2. 线程阻塞与唤醒:AQS 使用一个 FIFO 队列(双向链表)来管理等待获取锁的线程。

  3. 模板方法模式:AQS 提供了一些模板方法(如 tryAcquiretryRelease 等),子类可以通过重写这些方法来实现自定义的同步逻辑。


2. AQS 的核心组件

2.1 同步状态(state

state 是 AQS 的核心变量,用于表示同步器的状态。它的具体含义由子类定义。例如:

  • 在 ReentrantLock 中,state 表示锁的重入次数。

  • 在 Semaphore 中,state 表示剩余的许可数量。

  • 在 CountDownLatch 中,state 表示剩余的计数。

state 是一个 volatile 变量,保证了多线程环境下的可见性。

2.2 等待队列

AQS 使用一个 FIFO 队列(双向链表)来管理等待获取锁的线程。队列中的每个节点(Node)代表一个等待线程。节点有两种模式:

  • 独占模式(Exclusive):同一时刻只有一个线程可以获取锁(如 ReentrantLock)。

  • 共享模式(Shared):同一时刻可以有多个线程获取锁(如 Semaphore)。


3. AQS 的工作原理

AQS 的核心思想是通过 state 和等待队列来实现线程的阻塞与唤醒。以下是 AQS 的主要工作流程:

3.1 获取锁(acquire

  1. 线程调用 acquire(int arg) 方法尝试获取锁。

  2. 调用子类实现的 tryAcquire(int arg) 方法尝试获取锁。

    • 如果成功,则线程获取锁,继续执行。

    • 如果失败,则将线程包装成 Node 节点,加入等待队列。

  3. 线程在队列中自旋或阻塞,直到被前驱节点唤醒。

3.2 释放锁(release

  1. 线程调用 release(int arg) 方法释放锁。

  2. 调用子类实现的 tryRelease(int arg) 方法释放锁。

  3. 唤醒等待队列中的下一个线程。


4. AQS 的源码实现

4.1 同步状态(state

private volatile int state;

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);
}

4.2 等待队列

AQS 的等待队列是一个双向链表,节点定义如下:

static final class Node {
    volatile int waitStatus; // 等待状态
    volatile Node prev;      // 前驱节点
    volatile Node next;      // 后继节点
    volatile Thread thread;  // 等待线程
    Node nextWaiter;         // 共享模式或独占模式
}

4.3 获取锁(acquire

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
  • tryAcquire(int arg):子类实现,尝试获取锁。

  • addWaiter(Node mode):将当前线程包装成 Node 节点,加入等待队列。

  • acquireQueued(Node node, int arg):线程在队列中自旋或阻塞,直到获取锁。

4.4 释放锁(release

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
  • tryRelease(int arg):子类实现,尝试释放锁。

  • unparkSuccessor(Node node):唤醒等待队列中的下一个线程。


5. 如何使用 AQS 实现自定义同步器

以下是一个基于 AQS 实现的简单互斥锁:

import java.util.concurrent.locks.AbstractQueuedSynchronizer;

public class MyMutex {
    private static class Sync extends AbstractQueuedSynchronizer {
        @Override
        protected boolean tryAcquire(int arg) {
            if (compareAndSetState(0, 1)) { // 尝试将 state 从 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); // 将 state 重置为 0
            return true;
        }

        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }
    }

    private final Sync sync = new Sync();

    public void lock() {
        sync.acquire(1);
    }

    public void unlock() {
        sync.release(1);
    }
}

6. AQS 的应用场景

AQS 是许多并发工具类的基础,例如:

  • ReentrantLock:可重入锁。

  • Semaphore:信号量。

  • CountDownLatch:倒计时门闩。

  • ReentrantReadWriteLock:可重入读写锁。


7. 总结

AQS 是 Java 并发编程的核心框架,通过 state 和等待队列实现了高效的线程同步机制。理解 AQS 的工作原理和源码实现,有助于深入掌握 Java 并发编程,并能够基于 AQS 构建自定义的同步器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值