ReentrantLock

文章详细介绍了Java中的ReentrantLock,包括其作为可重入锁的特性,如何实现公平锁和非公平锁。在非公平锁中,新线程可以尝试直接抢占锁,而公平锁则需要确保没有其他等待线程。锁的释放过程涉及CAS修改状态变量和唤醒等待线程。AQS(AbstractQueuedSynchronizer)在其中起到了关键作用,管理锁的状态和等待队列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

它是一个可重入锁,实现了非公平锁和公平锁,加锁过程比较简单,判断当前锁对象的 state 值是否为 0,如果为 0 则抢占成功设置独占线程为当前线程,如果抢占失败,尝试将当前线程放到队列尾部,在放入之前会判断当前线程的上个线程是否为 head,如果是的话则再次尝试获取当前锁。如果失败则判断上个线程是否取消,如果取消则再次尝试获取锁,否则使用 LockSupport 锁住阻塞当前线程。

非公平锁

ReentrantLock lock = new ReentrantLock();
lock.lock();
lock.unlock();

public ReentrantLock() {
    // 默认构造函数会初始化锁类型为非公平锁
    sync = new NonfairSync();
}

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    final void lock() {
        // 这里是非公平体现, 新线程抢占更新锁
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

// aqs 方法设置阻塞当前线程,阻塞之前尝试获取锁
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

公平锁

 static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }
     public final void acquire(int arg) {
         if (!tryAcquire(arg) &&
             acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
             selfInterrupt();
     }
     
        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        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;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

可以看出实际上非公平锁中多做的几次自旋加锁的操作是因为服用了 aqs 为公平锁提供的方法。

锁释放

简单来看锁释放肯定包含两个操作

  1. 对 reenturnLock 锁的释放, 因为 reenturnLock 锁是通过维护 state ( 计数 ) 维护, 因此只需要 cas 修改该字段即可。
  2. 在等待链表队列中找到下一个阻塞线程并唤醒
public void unlock() {
    sync.release(1);
}

/* aqs 提供 */
public final boolean release(int arg) {
    // 维护 state 计数
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            // 维护成功后唤醒线程
            unparkSuccessor(h);
        return true;
    }
    return false;
}

/* aqs 提供 */
protected final boolean tryRelease(int releases) {
    // 维护的方式是修改重入次数
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        // 若为空则修改独占线程为 null
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

/* aqs 提供 */
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;
        // 可以看出唤醒线程时会判断 next 线程状态,校验失败将从尾部重新遍历
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        // 唤醒后的线程会卡在 aquire(1) 的方法中,尝试重新获取锁
        LockSupport.unpark(s.thread);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值