ReentrantLock 源码分析自我总结

创建的时候,默认使用的是 非公平队列;公平和非公平的区别就是入队的时候。非公平是不管队列中是否存在排队的节点直接尝试枪锁,而公平的则是会首先判断队列中存不存在排队的节点。

获取锁

final void lock() {
    // 非公平和公平的差异是 ,非公平上来会尝试获取锁
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else{
         //获取锁失败后执行此方法
           acquire(1);       
    }     
}

执行 acquire获取锁方法

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
// 1、tryAcquire 非公平的逻辑
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()) {
        //重入直接 将 state的值加即可
        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))

创建队列并加入到队列中 addWaiter

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) {
        // 代表当前队列已经初始化了.此处设置当前的前置节点,如果下面的cas失败,后面的 enq方法依旧会再重新修改.
        node.prev = pred; 
        // 因为存在多线程操作,所以使用CAS修改尾节点
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    // 如果队列没有初始化或者 设置尾节点失败后,下面这个方法保证入队
    enq(node);
    return node;
}
// 此方法通过自旋保证当前节点一定入队! 此方法返回的是之前的尾节点Node
private Node enq(final Node node) {
    for (;;) {
        // 得到当前的尾节点
        Node t = tail;
        if (t == null) { // Must initialize
            // 没有则初始化,通过cas保证只有一个线程初始化成功
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            // 设置当前的前置节点位尾节点
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                // 尝试设置尾节点,如果修改为尾节点成功之后,修改之前的尾节点的next为当前节点
                t.next = node;
                // 返回之前的尾节点
                return t;
            }
        }
    }
}

acquireQueued方法 在当前线程被park需要做几件事 :

如果当前节点的前驱节点为头节点,则再次尝试获取锁;
如果获取锁失败,需要修改当前节点的前驱节点为 Node.SIGNAL状态
park自己线程

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;
            }
            // 检查并移除队列中取消的节点,修改前驱节点的 waitStatus = Node.SIGNAL
            if (shouldParkAfterFailedAcquire(p, node)
            //阻塞当前线程
             && parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed){
            //如果操作失败则将此节点移除队列
            cancelAcquire(node);               
        }
    }
}

shouldParkAfterFailedAcquire 修改前驱节点的状态, 如果前驱节点为Node.CANCEL则 修改前驱节点为 正常的节点

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    // 得到前驱节点的状态
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        /*
         * This node has already set status asking a release
         * to signal it, so it can safely park.
         */
        return true;
    if (ws > 0) {
        /*
         * Predecessor was cancelled. Skip over predecessors and
         * indicate retry.
         */
         //如果当前前驱节点的状态 > 0 即为 Node.CANCELLED状态后需要移除节点
        do {
            //移除所有装状态为  Node.CANCELLED状态的节点
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        /*
         * waitStatus must be 0 or PROPAGATE.  Indicate that we
         * need a signal, but don't park yet.  Caller will need to
         * retry to make sure it cannot acquire before parking.
         */
         //修改当前前驱节点的状态为 -1
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

parkAndCheckInterrupt 阻塞当前线程

private final boolean parkAndCheckInterrupt() {
    // 直接阻塞当前节点线程
    LockSupport.park(this);
    return Thread.interrupted();
}

unparkSuccessor 唤醒当前节点的下一个非Node.CANCEL节点

private void unparkSuccessor(Node node) {
    /*
     * If status is negative (i.e., possibly needing signal) try
     * to clear in anticipation of signalling.  It is OK if this
     * fails or if status is changed by waiting thread.
     */
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
    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)
        //如果节点不为 null 唤醒正常的节点
        LockSupport.unpark(s.thread);
}

cancelAcquire 取消当前节点如果当前节点是 头节点则唤醒后面的正常节点

/**
* 移除当前节点;
* 1、检查当前节点的前驱节点是否存在 Node.CANCEL状态的节点,存在则移除这些节点.
* 2、如果当前节点是尾节点,修改当前节点的前驱节点为尾节点
* 3、如果是头节点则唤醒后面的正常节点.
*/
private void cancelAcquire(Node node) {
    // Ignore if node doesn't exist
    if (node == null)
        return;

    node.thread = null;

    // Skip cancelled predecessors
    Node pred = node.prev;
    while (pred.waitStatus > 0)
        node.prev = pred = pred.prev;

    // predNext is the apparent node to unsplice. CASes below will
    // fail if not, in which case, we lost race vs another cancel
    // or signal, so no further action is necessary.
    Node predNext = pred.next;

    // Can use unconditional write instead of CAS here.
    // After this atomic step, other Nodes can skip past us.
    // Before, we are free of interference from other threads.
    node.waitStatus = Node.CANCELLED;

    // If we are the tail, remove ourselves.
    if (node == tail && compareAndSetTail(node, pred)) {
        compareAndSetNext(pred, predNext, null);
    } else {
        // If successor needs signal, try to set pred's next-link
        // so it will get one. Otherwise wake it up to propagate.
        int ws;
        //代表当前节点不是 第一个节点[头节点不算]
        if (pred != head 
            && ((ws = pred.waitStatus) == Node.SIGNAL ||(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) 
            && pred.thread != null) {
            //得到当前节点的下一个节点. 
            Node next = node.next;
            // 如果当前节点的后置节点是正常节点直接将节点的next设置为前驱节点的next
            if (next != null && next.waitStatus <= 0) {
                compareAndSetNext(pred, predNext, next); 
            }
        } else {
            // 如果是第一个节点的话,会执行唤醒下一个节点
            unparkSuccessor(node);
        }
        node.next = node; // help GC
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值