ReemtrantLock原理(配合源码)

1.大体流程
在这里插入图片描述

![在这里插入图片描述](https://img-blog.csdnimg.cn/6d6b7549f2904d4e9d233e61439a6bac.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZOm6LGB7pCL,size_20,color_FFFFFF,t_70,g_se,x_162.源码分析
(1)构造方法
默认是非公平锁,以下都默认使用非公平锁进行分析。

public ReentrantLock() {
        sync = new NonfairSync();
    }

也可通过参数调整决定使用公平锁还是非公平锁:

 public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

(2)lock()
lock的基本过程:

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

调用同步器的lock方法:不看同步队列是否还有线程再排队,直接尝试去获取锁(cas地修改state),修改成功直接设定当前线程为独占线程拥有者否则再次获取:

final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

调用AQS同步器模板中的获取方法:

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

①tryAcquire(arg)
由重入锁定制的非公平锁:

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

如果sate为0,说明没有线程持有锁,获取成功后直接返回;如果当前线程就是独占线程拥有者,则重入次数+1。
如果获取失败,则准备进入同步队列排队等待。
先封装到Node,直接排队到队列尾部:
#为什么不再请求加入同步队列的时候再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) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

如果尾部为空,说明同步队列为空了,直接入队当head节点;如果有尾巴节点继续加在尾部,并返回其前继节点;
不过问题在于为什么还有可能队列非空,再次判断为节点呢?

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

接下来就会请求进入同步队列:

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

如果前继节点不是首节点,则应该找到一个状态为signal的前继节点作为前继节点:
也就是说允许前继节点状态为0,为什么呢?
其他情况,0和propagate状态

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);//也就是说允许前继节点状态为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;
    }

前继节点为signal状态,保证挂起后能被前继节点唤醒,挂起线程:

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);//线程将暂停在这里!
        return Thread.interrupted();// 返回中断标志并重置为false,不响应中断
    }

假设线程挂起后,被其他线程修改了中断标志,那么此线程将被唤醒,修改interrupted为true。由于重置了线程的中断标志,将可以再次进入循环挂起。这就是不响应中断。

if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;//此标记用于线程唤醒获取到锁后将线程中断标志还原(先前重置了中断标志位)

(3)unlock
调用同步器的release方法:
先尝试释放

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

所谓的释放就是减去重入次数,如果重入次数为0,说明锁已经释放了;

public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

此时当前线程还是头节点,修改当前节点的status为0,唤醒后继节点。唤醒后,被唤醒的线程将尝试获取锁,获取到锁后便成为新的头节点了。
情况1:为什么后继节点状态可能>0的?如果节点取消了或者是相应中断的节点会抛出异常,状态修改为1。比如说lockInterruptly() 方法就是相应中断的。
情况2:=0的节点不是signal状态,为啥会被唤醒?
节点进入同步队列的时候只对前继节点有要求,对本身节点的状态默认为0。而后加入的节点会将此节点置为-1,shouldParkAfterFailedAcquire方法中,compareAndSetWaitStatus(pred, ws, Node.SIGNAL);如果此节点是尾节点,没有后继节点将此节点的状态修改为-1,因此需要将状态为0的节点也唤醒。

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)
            LockSupport.unpark(s.thread);
    }

有几个问题值得思考:
#加入第一个获取锁的线程直接获取锁,后进的第一个线程成为头节点,谁来唤醒这个头节点呢?
在上面的唤醒方法里,这种情况会遍历队列,找到头节点唤醒;.

#头节点线程有几种状态?
第一个进来的头节点会被挂起,那唤醒后同样会p==head条件依然不成立,无法尝试获取锁啊??

private final boolean compareAndSetHead(Node update) {
        return unsafe.compareAndSwapObject(this, headOffset, null, update);
    }

(4)lockInterruptibly()
可响应中断的获取锁

public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

AQS模板方法acquireInterruptibly(int arg)

public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }
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;
        }

没有获取到锁,于是调用doAcquireInterruptibly(arg)(AQS模板方法)

private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();//响应中断
            }
        } finally {
            if (failed)
                cancelAcquire(node);//取消节点
        }
    }

取消节点:帮助后继节点寻找signal为-1的节点,并置空自身。如果是头节点就唤醒后继节点。

 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;
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
                unparkSuccessor(node);
            }

            node.next = node; // help GC
        }
    }

取消的节点线程又会去哪里呢?
lockInterruptibly() throws InterruptedException ,向上抛异常,如果没有人处理线程就结束(不会影响其他线程);

(5) tryLock()
只是尝试获取,获取不到锁就做其他事,不会排队。

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

3.等待队列
增加一个等待队列,主要作用是替代sychonized的object.wati和sign方法的;并且一个同步队列可以增加多个不同的等待队列,大大的增强了灵活性;
可以参考以下模型:
无线挂起的
在这里插入图片描述
时间到自行唤醒的:
在这里插入图片描述

 final ConditionObject newCondition() {
            return new ConditionObject();
        }

内部维护了一个头节点和尾节点的单向链表,添加节点时使用的是newWaiter(同步队列使用的next和prev);

private static final long serialVersionUID = 1173984872572414699L;
        /** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;

另外两个常量

/*
         * For interruptible waits, we need to track whether to throw
         * InterruptedException, if interrupted while blocked on
         * condition, versus reinterrupt current thread, if
         * interrupted while blocked waiting to re-acquire.
         */

        /** Mode meaning to reinterrupt on exit from wait */
        private static final int REINTERRUPT =  1;
        /** Mode meaning to throw InterruptedException on exit from wait */
        private static final int THROW_IE    = -1;

(1)await
挂起

public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();//加入等待队列
            int savedState = fullyRelease(node);//释放当前线程获取的锁
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);//挂起当前线程在队列等待(如果被中断会被直接唤醒,线程的中断机制)
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)//正常唤醒检测到已经再同步队列中会跳出循环
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

加入等待队列

 private Node addConditionWaiter() {
            Node t = lastWaiter;
            // If lastWaiter is cancelled, clean out.
            if (t != null && t.waitStatus != Node.CONDITION) {
                unlinkCancelledWaiters();
                t = lastWaiter;
            }
            Node node = new Node(Thread.currentThread(), Node.CONDITION);
            if (t == null)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;
            return node;
        }

如果被正常唤醒(在挂起过程中未被中断),节点会加入到同步队列;

/**
         * Checks for interrupt, returning THROW_IE if interrupted
         * before signalled, REINTERRUPT if after signalled, or
         * 0 if not interrupted.
         */
        private int checkInterruptWhileWaiting(Node node) {
            return Thread.interrupted() ?
                (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
                0;
        }
/**
     * Transfers node, if necessary, to sync queue after a cancelled wait.
     * Returns true if thread was cancelled before being signalled.
     *
     * @param node the node
     * @return true if cancelled before the node was signalled
     */
    final boolean transferAfterCancelledWait(Node node) {
        if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
            enq(node);//如果是再唤醒前就被中断,此时应该可以入队成功
            return true;
        }
        /*
         * If we lost out to a signal(), then we can't proceed
         * until it finishes its enq().  Cancelling during an
         * incomplete transfer is both rare and transient, so just
         * spin.
         */
        while (!isOnSyncQueue(node))//如果在唤醒之后中断,会返回false
            Thread.yield();
        return false;
    }

否则,如果是再唤醒前中断的,需要抛出异常;
如果是唤醒后中断的,不需要抛出异常只需要修改中断标志;

 /**
         * Throws InterruptedException, reinterrupts current thread, or
         * does nothing, depending on mode.
         */
        private void reportInterruptAfterWait(int interruptMode)
            throws InterruptedException {
            if (interruptMode == THROW_IE)
                throw new InterruptedException();
            else if (interruptMode == REINTERRUPT)
                selfInterrupt();
        }

(2)signal
唤醒

 public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }
private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }

从等待队列加入到同步队列

Transfers a node from a condition queue onto sync queue
final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))//每次加入节点将前尾巴节点状态置为signal
            LockSupport.unpark(node.thread);
        return true;
    }

加入到同步队列尾部,如果前继节点(同步队列的尾节点)被取消了且状态更改成功了,就唤醒当前线程;如果状态不大于0就直接唤醒线程;enq也就相当于只是加在了同步队列后面,并没有挂起或者请求的动作;

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

唤醒后将在同步队列排队;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值