J.U.C - ReentrantLock源码分析 - 笔记

196 阅读4分钟

写在前面

CAS(Compare and Swap)

比较并交换。对一个值进行赋值的原子操作,底层调用的是sun.misc.Unsafe类的一个native方法,通过JNI调用CPU底层指令实现。

重入

同一个线程可以多次获取同一把锁。

AQS队列

遵循FIFO原则的一个双向链表(Node),未抢占到锁的线程会以尾插法的方式放入链表中。

手把手一步一步进行源码分析

public static void main(String[] args) {
    ReentrantLock lock = new ReentrantLock();
    try {
        lock.lock();
    } finally {
        lock.unlock();
    }
}

一、lock分析

通过构造方法可以看出,默认采用的是非公平锁
public ReentrantLock() {
    sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

假设同时有两个线程ThreadAThreadB同时调用lock方法
// 原始代码
final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}
// 加了注释后的代码
final void lock() {
    // 通过CAS抢占锁, 假设ThreadA成功将state的值从0设置为1
    if (compareAndSetState(0, 1))
        // 抢占成功, 设置exclusiveOwnerThread为ThreadA线程       
        setExclusiveOwnerThread(Thread.currentThread());
        // 抢占锁成功, 退出lock方法, 执行ThreadA的业务逻辑
    else
        // ThreadA 修改 state状态为1之后, ThreadB设置失败执行以下逻辑
        acquire(1);
}
进入acquire(1)方法分析
// 原始代码
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
// 加了注释后的代码
public final void acquire(int arg) {
    // 预分析
    /* !tryAcquire: 返回true时,if 条件之后没有其他逻辑,我们可以猜测,应该是获取到了锁,
                    至于如何获取到锁的,后面继续进行源码分析 */
    // addWaiter: 初始化链表的头尾节点和创建当前线程的node节点并设置为尾节点(尾插法)
    /* acquireQueued: 自选通过上面的tryAcquire抢占锁, 如果抢占失败,
                      则阻塞当前线程(通过LockSupport.park()), 具体后面分析 */
if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        // 中断标记传递
        selfInterrupt();
}
进入tryAcquire方法后调用的是非公平锁的nonfairTryAcquire方法, 所以直接看nonfairTryAcquire
// 算了还是粘出来吧
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
// 原始代码
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    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;
}
// 加了注释的代码
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread(); // 获取当前线程
    int c = getState(); // 获取state值
    // 如果为0时, 说明ThreadA已经执行玩逻辑调用unlock方法
    if (c == 0) {
        // cas抢占锁, 这段代码熟悉吧! 和上面lock方法一模一样的操作
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true; // 返回true, 抢占锁成功
        }
    }
    /* 如果不为0时,则判断exclusiveOwnerThread是否和当前线程是同一个线程, 如果是就代表重入,
       重入逻辑就做了state = state + acquires 进行累加重入次数 */
    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; // 返回false, 上一个放是做了取反的,我们继续往下分析
}
addWaiter(Node.EXCLUSIVE) 分析
// 原始代码
private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}
// 加了注释的代码
private Node addWaiter(Node mode) {
    // 创建当前线程的一个节点(此时这个节点和链表还没有任何关系)
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    // 获取链表的尾节点
    Node pred = tail;
    // 如果尾节点不为空
    if (pred != null) {
        // 将当前线程的节点的上一个节点设置成尾节点
        node.prev = pred;
        // CAS操作, 将尾节点设置成当前线程的节点, 知道为什么不先将尾节点的下一个节点设置成node吗?
           因为当cas操作失败之后, 不会影响到原来链表的结构。(好吧,你肯定知道,我就喜欢多嘴)
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    // 初始化头尾节点, 并自选通过cas尾插当前线程节点(上面不进if 或者 cas 失败我不能不管是吧)
    enq(node);
    return node;
}
enq(node) 分析
// 原始代码
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;
            }
        }
    }
}
// 加了注释的代码
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        // tail == null 时, cas初始化头尾节点, 设置失败则for自旋重新设置
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            // cas设置当前线程节点为尾节点, 设置失败则for自旋重新设置
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 分析
// 原始代码
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);
    }
}
// 加了注释的代码
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            // 获取当前线程节点的上一个节点
            final Node p = node.predecessor();
            // 如果上一个节点为head节点, 通过上面分析tryAcquire方法进行cas抢占锁
            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);
    }
}
shouldParkAfterFailedAcquire(p, node) 分析
// 原始代码
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        return true;
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}
// 加了注释的代码
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        return true; // 只有节点waitStatus=SIGNAL时才会返回true
    if (ws > 0) {
        do {
            // 循环去除处于CANCELLED状态下的节点
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        // cas设置pred.waitStatus为SIGNAL
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}
parkAndCheckInterrupt() 分析
private final boolean parkAndCheckInterrupt() {
    // 阻塞当前线程    LockSupport.park(this);
    return Thread.interrupted();
}

二、unlock分析

public void unlock() {
    sync.release(1);
}
public final boolean release(int arg) {
    // 递减state(递减重入次数), 为0时返回true
    if (tryRelease(arg)) {
        // 获取头节点
        Node h = head;
        if (h != null && h.waitStatus != 0)
            // 唤醒处于阻塞状态下的线程
            unparkSuccessor(h);
        return true;
    }
    return false;
}