非公平锁的上锁过程

2020-03-19  本文已影响0人  kele2018

1、lock

final void lock() {
            //一进来就可以尝试获取锁  不用管自己前面是否已经有人在排队
            if (compareAndSetState(0, 1))
                //拿到锁且可以用   就在锁上标记一下  哪个线程在用这把锁
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
  }

2、acquire

public final void acquire(int arg) {
        /**
         * tryAcquire(arg)这个方法是留给子类覆盖的   给我们一个自定义获取锁逻辑的机会
         * addWaiter(Node.EXCLUSIVE)这个方法会把当前线程封装成一个Node对象,然后添加到链表(双向)中
         */
        if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)){
            selfInterrupt();
        }
    }

2.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()) {  //ReentranLock为可重入锁
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

2.2、addWaiter

private Node addWaiter(Node mode) {
        //把当前没有获取锁的线程封装成一个node对象
        Node node = new Node(Thread.currentThread(), mode);
        //拿到尾部节点
        Node pred = tail;
        if (pred != null) {
            //当前节点的prev指向尾部节点
            node.prev = pred;
            //设置尾部节点为当前节点
            if (compareAndSetTail(pred, node)) {
                //设置原先尾部节点的next为当前节点
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

2.2.1、 enq

private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) {
                /**
                 * 初始化head节点和tail节点,让它们都指向一个空节点
                 * 这就相当于把第一个位置空出来,以后哪个线程坐在这个位置上哪个线程就工作,而有资格获取锁的线程永远是排在第一个位置后的线程
                 */
                if (compareAndSetHead(new Node())){
                    tail = head;
                }
            } else {
                /**
                 * 这里就是快速入链表的过程,完成三件事
                 * 1、尾部节点指向当前节点
                 * 2、原先的尾部节点的next节点指向当前节点
                 * 3、当前节点的prev节点指向原先的尾部节点
                 */
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

2.3、acquireQueued

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                //返回node的prev
                final Node p = node.predecessor();
                //只有排在head节点之后的线程才能尝试去获取锁
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                /**
                 * shouldParkAfterFailedAcquire(p, node) 这个方法决定了当前线程是否要park
                 */
                if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()){
                    interrupted = true;
                }

            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

2.3.1 shouldParkAfterFailedAcquire


private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        //ws=-1
        if (ws == Node.SIGNAL)
            //当一个节点的上一个节点的waitStatus等于-1的时候,当前节点需要阻塞
            return true;
        if (ws > 0) {

            do {
                /**
                 * 把当前节点的上一个节点从链表中拿掉
                 * 说明当一个节点的waitStatus大于0的时候,说明这个节点不想再等待了,所以就应该把它从链表中拿掉
                 */
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {

            /**
             * 更新上一个节点的waitStatus为-1
             *
             * 说明当上一个节点的waitStatus除了上面两种情况    都要更新waitStatus为-1
             *
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
上一篇下一篇

猜你喜欢

热点阅读