JAVA八股之源码解析----ReentrantLock原理源码解析

 ReentrantLock 的构造方法可以指定是否使用公平锁:

public ReentrantLock() {
    this.sync = new NonfairSync(); // 默认是非公平锁
}

public ReentrantLock(boolean fair) {
    this.sync = fair ? new FairSync() : new NonfairSync(); // 根据参数选择锁的类型
}

非公平锁 (NonfairSync)

tryLock 方法

final boolean tryLock() {
    Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) { // 锁未被占用
        if (compareAndSetState(0, 1)) { // 尝试通过 CAS 获取锁
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) { // 当前线程已持有锁
        setState(c + 1); // 增加重入计数
        return true;
    }
    return false; // 获取失败
}
  • 核心特点
    • 非公平锁允许线程插队,不需要检查等待队列是否有其他线程。
    • 如果锁未被占用,通过 CAS 直接尝试获取。
    • 如果锁已被当前线程持有,支持重入操作,直接增加 state 值。

lock 方法

final void lock() {
    if (!initialTryLock()) { // 尝试快速获取锁
        acquire(1);          // 获取失败,则进入 AQS 队列
    }
}
initialTryLock 方法
final boolean initialTryLock() {
    Thread current = Thread.currentThread();
    if (compareAndSetState(0, 1)) { // CAS 尝试获取锁
        setExclusiveOwnerThread(current);
        return true;
    } else if (getExclusiveOwnerThread() == current) { // 当前线程已持有锁
        setState(getState() + 1); // 增加重入计数
        return true;
    }
    return false; // 获取失败
}
  • 核心特点
    • 非公平锁先尝试快速获取锁,通过 initialTryLock 方法直接操作 state
    • 如果快速获取失败,进入 AQS 的等待队列,由 AQS 负责排队、阻塞和唤醒。

acquire()

public final void acquire(int arg) {
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
        selfInterrupt();
    }
}
解析
  1. 尝试获取锁

    • 首先调用 tryAcquire(arg) 尝试获取锁。
    • 非公平锁公平锁都通过各自的 tryAcquire 方法实现不同的获取逻辑。
  2. 加入等待队列

    • 如果 tryAcquire(arg) 返回 false,则调用 addWaiter(Node.EXCLUSIVE) 方法将当前线程加入等待队列。
    • 等待队列是基于 AbstractQueuedSynchronizerNode 实现的 FIFO 结构。
  3. 进入队列等待

    • 调用 acquireQueued 方法,在队列中等待唤醒并重新尝试获取锁。
  4. 中断线程

    • 如果线程在等待过程中被中断,调用 selfInterrupt 方法设置线程的中断状态。

tryAcquire(arg)方法

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) { // 锁未被占用
        if (compareAndSetState(0, acquires)) { // CAS 尝试获取锁
            setExclusiveOwnerThread(current); // 设置当前线程为锁的持有者
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) { // 当前线程已持有锁
        setState(c + acquires); // 增加重入次数
        return true;
    }
    return false; // 获取失败
}

addWaiter 方法

private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode); // 创建一个新的节点
    Node pred = tail; // 获取队列尾节点
    if (pred != null) {
        node.prev = pred; // 当前节点的前驱指向队尾
        if (compareAndSetTail(pred, node)) { // CAS 设置新节点为队尾
            pred.next = node; // 前驱节点的 next 指向当前节点
            return node;
        }
    }
    enq(node); // 如果失败,调用 enq 方法完成入队
    return node;
}
  • 功能:将当前线程封装成节点,并添加到等待队列的尾部。
  • 快速入队:如果队列已初始化,通过 CAS 快速将新节点插入队列尾部。
  • 完整入队:如果快速插入失败,调用 enq 方法完成完整的入队操作。

acquireQueued 方法

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true; // 标记是否获取失败
    try {
        boolean interrupted = false; // 标记线程是否被中断
        for (;;) {
            final Node p = node.predecessor(); // 获取前驱节点
            if (p == head && tryAcquire(arg)) { // 如果前驱是头节点且成功获取锁
                setHead(node); // 将当前节点设置为头节点
                p.next = null; // 断开前驱节点的 next 引用,帮助 GC
                failed = false; // 标记获取成功
                return interrupted; // 返回中断状态
            }
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                interrupted = true; // 如果需要挂起线程并检测到中断,标记中断状态
        }
    } finally {
        if (failed)
            cancelAcquire(node); // 如果失败,从等待队列中移除节点
    }
}


unlock 方法

final void unlock() {
    sync.release(1); // 调用 AQS 的 release 方法
}
AQS 的 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 方法
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread()) {
        throw new IllegalMonitorStateException(); // 非锁持有线程调用 unlock 抛出异常
    }
    boolean free = c == 0; // 判断锁是否完全释放
    if (free) {
        setExclusiveOwnerThread(null); // 清除持有线程
    }
    setState(c); // 更新锁的状态
    return free;
}
  • 核心特点
    • 调用线程必须是锁的持有者,否则抛出 IllegalMonitorStateException
    • 减少 state 值(释放锁或减少重入计数),当 state == 0 时完全释放锁。
    • 如果有等待线程,唤醒队列中的第一个线程。

公平锁 (FairSync)

tryLock 方法

同非公平锁的该方法


lock 方法

final void lock() {
    acquire(1); // 调用 AQS 的 acquire 方法
}
  • 核心特点
    • 公平锁的 lock 方法不支持快速获取锁。
    • 直接调用 AQS 的 acquire 方法,将当前线程加入等待队列,由 AQS 负责管理线程的排队、阻塞和唤醒。

acquire方法跟非公平锁的一样,其中只是tryAcquire方法不一样

公平锁的tryAcquire方法:

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) { // 锁未被占用
        if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
            // 检查队列是否有前置线程,如果没有,CAS 尝试获取锁
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) { // 当前线程已持有锁
        setState(c + acquires); // 增加重入次数
        return true;
    }
    return false; // 获取失败
}

unlock 方法

公平锁的 unlock 方法和非公平锁相同:

  • 调用 AQS 的 release 方法尝试释放锁。
  • 减少 state 值,当 state == 0 时完全释放锁。
  • 如果有等待线程,唤醒队列中的第一个线程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值