并发编程-ReentrantLock-公平锁/非公平锁源码分析

1、公平锁执行逻辑

查看ReentrantLock的lock方法

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

继续查看FairSync的lock方法

final void lock() {
    //调用了AbstractQueuedSynchronizer的acquire(int arg)方法    
    acquire(1);
}

继续查看AbstractQueuedSynchronizer的acquire方法

public final void acquire(int arg) {
    //tryAcquire(arg)判断
    //调用了FairSync的tryAcquire(int acquires)方法
    //addWaiter(Node.EXCLUSIVE), arg)创建阻塞队列节点
    //acquireQueued(addWaiter(Node.EXCLUSIVE), arg)调用LockSupport.park阻塞队列
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

查看FairSync的tryAcquire方法

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    //state=0未被加锁
    if (c == 0) {
    //!hasQueuedPredecessors()表示阻塞队列为空或将要执行的阻塞队列节点线程就是当前线程
    //compareAndSetState(0, acquires) 调用cas加锁
    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;
}

继续查看AbstractQueuedSynchronizer的AbstractQueuedSynchronizer()方法

public final boolean hasQueuedPredecessors() {
    Node t = tail;
    Node h = head;
    Node s;
    //h != t 当头和尾都为空,也就是阻塞队列为空,此时返回false
    //队列不为空,阻塞队列要抢占锁的节点的线程和当前线程相同,则((s = h.next) == null || s.thread != Thread.currentThread())为false
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}
线程调用 lock()
└─ 1. 直接执行 acquire(1) 流程(无首次 CAS 插队):
   ├─ 1.1 调用 tryAcquire(1) 尝试抢锁:
   │  ├─ 检查 state:
   │  │  ├─ state=0:先检查队列(hasQueuedThreads()):
   │  │  │  ├─ 队列无等待线程:CAS(state=0→1)
   │  │  │  │  ├─ 成功:setExclusiveOwnerThread(当前线程),返回 true,acquire 流程结束;
   │  │  │  │  └─ 失败:返回 false,进入入队逻辑;
   │  │  │  └─ 队列有等待线程:直接返回 false,进入入队逻辑;
   │  │  └─ state≠0:检查是否是当前线程持有锁(重入):
   │  │     ├─ 是:state+1(重入计数),返回 true,acquire 流程结束;
   │  │     └─ 否:返回 false,进入入队逻辑;
   └─ 1.2 入队逻辑(addWaiter + acquireQueued):
      ├─ addWaiter:将当前线程封装为 Node,尾插入 AQS 同步队列;
      └─ acquireQueued:线程挂起,等待前驱节点唤醒后再次尝试抢锁,直到成功。

 总结:公平锁的加锁逻辑:先判断锁状态是否为0,为0再判断ReentrantLock的阻塞队列是否为空或队列将要获取锁的节点线程是否等于当前线程,是说明当前线程可以通过cas加锁,否则进入阻塞队列排队。

========================================================================================================

 2、非公平锁逻辑

查看ReentrantLock的lock方法

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

继续查看NonfairSync的lock方法

final void lock() {
    //非公平锁上来就会通过compareAndSetState(0, 1)尝试加锁,加锁成功通过setExclusiveOwnerThread(Thread.currentThread())设置独占锁模式下的线程    
    if (compareAndSetState(0, 1))
    setExclusiveOwnerThread(Thread.currentThread());
    else
    //compareAndSetState(0, 1)加锁失败后,会通过cas再次加锁,加锁失败入阻塞队列
    acquire(1);
}

查看AbstractQueuedSynchronizer的acquire方法

public final void acquire(int arg) {
     //tryAcquire(arg)再次通过cas加锁逻辑
    //acquireQueued(addWaiter(Node.EXCLUSIVE), arg)加锁失败,创建阻塞队列节点并入队,调用LockSupport.park阻塞
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

继续查看NonfairSync的tryAcquire方法

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


final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    //c==0 证明锁未被抢占
    if (c == 0) {
    //通过compareAndSetState(0, acquires)尝试加锁,加锁成功调用 setExclusiveOwnerThread(current)设置独占锁线程
    if (compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(current);
        return true;
    }
    }//c != 0 证明锁已被抢占,但是current == getExclusiveOwnerThread()证明当
    else if (current == getExclusiveOwnerThread()) {
    int nextc = c + acquires;
    if (nextc < 0) // overflow
        throw new Error("Maximum lock count exceeded");
    setState(nextc);
    return true;
    }
    return false;
}

 继续查看 acquireQueued(addWaiter(Node.EXCLUSIVE), arg),包含加锁和入队逻辑

//作用:
//1、线程阻塞之前,判断当前节点是不是队列里的首节点,如果是再尝试加一次锁
//2、持有锁的线程释放锁后,调用LockSupport.unpark(s.thread);唤醒阻塞队列里首节点
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; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
//包装节点,再执行入队逻辑
private Node addWaiter(Node mode) {
    //把当前线程包装成node节点
    Node node = new Node(Thread.currentThread(), mode);
    Node pred = tail;
    //如果队列存在,则直接把当前节点设置为尾结点
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    //如果此时还不存在队列,执行enq入队逻辑
    enq(node);
    return node;
}
//入队逻辑,生成双向链表,最终通过return节点终止
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

总结:非公平锁的加锁逻辑:上来先通过cas尝试加锁,加锁失败,此时判断锁状态为0再通过cas再次加锁,加锁失败创建阻塞队列节点并入队。

线程调用 lock()
├─ 1. 首次 CAS 抢锁:直接 CAS(state=0→1)
│  ├─ 成功:setExclusiveOwnerThread(当前线程),加锁完成;
│  └─ 失败:进入 acquire(1) 流程;
└─ 2. 执行 acquire(1)(AQS 核心流程):
   ├─ 2.1 调用 nonfairTryAcquire(1) 再次尝试抢锁:
   │  ├─ 检查 state:
   │  │  ├─ state=0:无视队列,直接 CAS(state=0→1)
   │  │  │  ├─ 成功:setExclusiveOwnerThread(当前线程),返回 true,acquire 流程结束;
   │  │  │  └─ 失败:返回 false,进入入队逻辑;
   │  │  └─ state≠0:检查是否是当前线程持有锁(重入):
   │  │     ├─ 是:state+1(重入计数),返回 true,acquire 流程结束;
   │  │     └─ 否:返回 false,进入入队逻辑;
   └─ 2.2 入队逻辑(addWaiter + acquireQueued):
      ├─ addWaiter:将当前线程封装为 Node,尾插入 AQS 同步队列;
      └─ acquireQueued:线程挂起,等待前驱节点唤醒后再次尝试抢锁,直到成功。

 

=========================================================解锁逻辑=====================================================

1、执行unlock方法

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

2、继续查看AQS的释放锁方法release

/**
 * 1、执行tryRelease(arg)释放锁成功
 * 2、判断阻塞队列不为空,调用 unparkSuccessor(h)唤醒阻塞队列的首节点
 */
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

3、继续查看tryRelease(arg)方法

/**
 * 1、获取锁状态-1,并判断结果是否为0
 *         是,则执行1、持有锁线程为null,2、设置锁状态为0,3、返回true
 */
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;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

4、释放锁成功,则继续执行unparkSuccessor(h)

/**
 * 1、获取阻塞队列首节点的等待状态,小于0则cas更新为0
 * 2、调用LockSupport.unpark(s.thread)唤醒等待线程
 */
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 <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);
}

总结:解锁逻辑,获取锁状态-1是否大于0,是,则为重入锁,cas更新锁状态-1。否,则cas更新锁状态为0,设置持有锁线程为null,调用LockSupport.unpark(t)唤醒阻塞队列的首节点。

posted @ 2023-06-13 23:07  西风51668  阅读(44)  评论(0)    收藏  举报