深入理解 Java AQS:ReentrantLock 的底层实现详解

Java 的 AbstractQueuedSynchronizer(AQS)是一个用于构建同步器(如锁和信号量)的框架。它提供了一种实现线程同步的机制,通过管理一个先进先出(FIFO)的线程等待队列来控制对共享资源的访问。ReentrantLock 是基于 AQS 实现的一个可重入锁。下面我将详细解释 AQS 的原理以及 ReentrantLock 的实现方法。

图片[1]_深入理解 Java AQS:ReentrantLock 的底层实现详解_知途无界

AQS 原理

  1. 状态(State)
    • AQS 内部维护一个 state 变量,用于表示共享资源的使用状态。子类通过实现特定的方法来管理这个状态。
    • 对于独占模式,state 为 0 表示资源可用,大于 0 表示资源被占用。
    • 对于共享模式,state 的具体含义由子类定义,但通常用于表示可用资源的数量。
  2. 等待队列(CLH 队列)
    • AQS 使用一个双向链表来管理等待获取资源的线程。
    • 每个节点包含线程引用、等待状态等信息。
    • 线程在获取资源失败时会被封装成一个节点并加入队列尾部。
  3. 独占模式和共享模式
    • 独占模式:只有一个线程能获取资源,如 ReentrantLock。
    • 共享模式:多个线程可以同时获取资源,如 Semaphore 和 CountDownLatch。
  4. 模板方法
    • AQS 提供了一些需要子类实现的模板方法,如 tryAcquiretryReleasetryAcquireSharedtryReleaseShared 等。
    • 子类通过实现这些方法定义具体的同步策略。

ReentrantLock 实现方法

ReentrantLock 是一个可重入的独占锁,基于 AQS 实现。以下是其关键实现点:

  1. 状态管理
    • ReentrantLock 使用 AQS 的 state 变量来表示锁的重入次数。
    • 初始状态为 0,表示锁未被占用。每次获取锁时,state 加 1;每次释放锁时,state 减 1。
  2. 获取锁(lock() 方法)
    • 调用 AQS 的 acquire 方法,尝试获取锁。
    • tryAcquire 方法在 ReentrantLock 中被实现为:如果当前线程是锁的持有者,则增加重入计数并返回成功;否则,检查锁是否可用,若可用则设置锁的持有者为当前线程并返回成功。
  3. 释放锁(unlock() 方法)
    • 调用 AQS 的 release 方法,尝试释放锁。
    • tryRelease 方法在 ReentrantLock 中被实现为:减少重入计数,如果计数为 0,则释放锁并返回成功。
  4. 非公平锁与公平锁
    • 非公平锁:线程在尝试获取锁时,可能会直接插队获取锁,而不是按照等待队列的顺序。
    • 公平锁:线程严格按照等待队列的顺序获取锁。
    • 通过构造函数可以选择使用非公平锁或公平锁。

示例代码

以下是一个简单的 ReentrantLock 使用示例:

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void performTask() {
        lock.lock();
        try {
            // 临界区代码
            System.out.println(Thread.currentThread().getName() + " is executing.");
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ReentrantLockExample example = new ReentrantLockExample();
        Runnable task = example::performTask;

        Thread thread1 = new Thread(task, "Thread-1");
        Thread thread2 = new Thread(task, "Thread-2");

        thread1.start();
        thread2.start();
    }
}

在这个示例中,ReentrantLock 用于确保同一时间只有一个线程可以执行 performTask 方法中的临界区代码。

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞61 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容