非公平锁加锁主要流程解读

 

一、方法一

  

final void lock() {
    //第一个线程获取锁,如果成功,则直接返回,这种情况是最快的 
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
       //如果不是第一个线程获取锁,则加锁流程
        acquire(1);
}

 

二、方法二

 

// 3个主要方法 tryAcquire、addWaiter、  acquireQueued
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

三、方法三

  

//非公平锁,尝试获取锁
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;
}

四、方法四

  

//节点放入到队列的过程
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;
    //如果tail不为空,即已经有队列了
    if (pred != null) {
            //当前线程的节点,前置节点设置为原来链表的tail
        node.prev = pred;
        //如果队列此时尾部节点还是pred,则设置node节点为尾部节点
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    //当前锁中还没有队列,创建一个新队列
    enq(node);
    return node;
}

五、方法五

   

//构建一个队列,用来排队
private Node enq(final Node node) {
    //循环2次创建一个队列
    for (;;) {
        Node t = tail;
         //tail节点为空,说明 还没有队列
        if (t == null) { // Must initialize ,第一个循环会进到这个里面
            //创建一个空的node,并且空节点设置为头节点
            if (compareAndSetHead(new Node()))
               //tail 也指向这个空节点
                tail = head;
        } else {
            //当前节点的prev ,指向 前面 空的头节点
            node.prev = t;
            // 把 当前线程的 node节点,放到队列的尾部
            if (compareAndSetTail(t, node)) {
                // 把原tail节点的next 指向当前节点,即 空的node节点(头节点)的next指向当前节点
                t.next = node;
                return t;
            }
        }
    }
}

到此为止,锁中的队列结构为:

 

 六,方法六

  

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);
    }
}

 

七、方法七 

  

设置当前节点的前一个节点waitStatus 状态为 SIGNAL ,这样前一个节点释放锁以后才会唤醒后面的线程节点
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.
         */
        do {
            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.
         */
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

 

 八、方法八-线程阻塞

 

private final boolean parkAndCheckInterrupt() {
    //线程阻塞,当被唤醒以后会接着这个地方继续执行
    LockSupport.park(this);
    return Thread.interrupted();
}

 

 到此线程的加锁过程完成

 

 

posted @ 2020-09-23 17:01  码来  阅读(257)  评论(0编辑  收藏  举报