【JUC源码专题】ReentrantLock 核心源码分析(JDK8)

加锁流程概述

公平锁和非公平锁加锁流程不同处

公平锁

  • 判断当前是否有线程持有锁
    • 若有,判断是否是当前线程,若是当前线程则重入。重入次数超过上限(int 最大值)则抛异常。
      • 若不是当前线程持有锁,则将当前线程封装成 Node 进入队列排队。
    • 若没有线程持有锁,则判断队列中是否有其他线程正在排队。
      • 若没有线程排队或者当前线程排在第一个,则基于CAS的方式将 state 修改为1。若修改成功则表示获取锁成功,将当前线程设置到 exclusiveOwnerThread,代表是当前线程持有锁资源。 设置中断标记位selfInterrupt()。
      • 若有其他线程在排队或CAS修改失败,将当前线程封装成 Node 进入队列排队。

非公平锁

  • 基于CAS的方式,尝试将state从0改为1
    • 若修改成功则表示获取锁成功,将当前线程设置到 exclusiveOwnerThread,代表是当前线程持有锁资源。
    • 若修改失败则判断当前是否有线程持有锁
      • 若没有线程持有锁,则基于CAS的方式,尝试将state从0改为1。若修改成功则表示获取锁成功,将当前线程设置到 exclusiveOwnerThread,代表是当前线程持有锁资源。设置中断标记位selfInterrupt()。
      • 若有线程持有锁则判断是否是当前线程,若是当前线程则重入。重入次数超过上限(int 最大值)则抛异常。
        • 若有其他线程在排队或CAS修改失败,将当前线程封装成 Node 进入队列排队。

公平锁和非公平锁加锁流程相同处

  1. 线程封装成 Node 进入队列排队
  2. 判断当前结点的前继结点是否是 head 结点,若是则尝试获取锁
  3. 若不是则将当前结点挂起等待唤醒

lock() 方法公平锁和非公平锁流程图

在这里插入图片描述

加锁流程源码解析

lock()

ReentrantLock 中的 lock() 方法分为公平锁和非公平锁两种实现。

   public void lock() {
	    // sync 是 ReentrantLock 的一个内部类
	    // abstract static class Sync extends AbstractQueuedSynchronizer
        sync.lock();
    }

公平锁实现

  static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // 查看队列中是否有正在排队的线程:1. 没线程排队 2. 有线程排队,但是第一个(不包括伪头)就是当前线程
                if (!hasQueuedPredecessors() &&
		            // 基于 CAS 的方式,尝试将 state + 1(从 0 改成 1)
                    compareAndSetState(0, acquires)) {
                    // 设置当前持有锁的线程是当前线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 当前持有锁的线程就是当前线程,重入
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                // 判断是否溢出,可重入次数最大为 int 最大值。
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
hasQueuedPredecessors()
    // 查看是否有除了当前线程以外的线程在队列中排队
    // 返回 false 说明没有线程在排队,可以竞争锁
    public final boolean hasQueuedPredecessors() {
        // 尾结点
        Node t = tail; 
        // 头结点
        Node h = head;
        Node s;
        // 返回 false 的两种情况:
        // 1 h == t 即头尾结点都指向伪头结点
        // 2 当前排队的第一个结点(除了伪头结点)就是当前线程
        // s = h.next; 
        // s != null && s.thread == Thread.currentThread()
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

非公平锁实现

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
  
        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
         final void lock() {
             // 先基于 CAS 的方式,尝试将 state 修改为 1
             if (compareAndSetState(0, 1))
                 // 获取锁资源成功并将当前线程设置到 exclusiveOwnerThread,代表是当前线程持有锁资源
                 // private transient Thread exclusiveOwnerThread 属性在 AQS 的父类 AbstractOwnableSynchronizer 中
                 // 此处没有设置中断标志位
                 setExclusiveOwnerThread(Thread.currentThread());
             else
                 // 尝试获取锁资源
                 acquire(1);
         }
         
         protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
         }
     }
        // 非公平锁实现
        // acquire(1) -> tryAcquire(1) -> nonfairTryAcquire(1) 故此处 acquires 的值为 1
        final boolean nonfairTryAcquire(int acquires) {
            // 获取当前线程
            final Thread current = Thread.currentThread();
            // 获取 state 属性
            int c = getState();
            // 判断 state 是否为 0,说明持有锁的线程释放了锁资源
            if (c == 0) {
                // 再次通过 CAS 将 state 从 0 修改为 1 
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    // 抢到锁了返回 true
                    return true;
                }
            }
            // 判断持有锁的线程是否是当前线程
            else if (current == getExclusiveOwnerThread()) {
                // 是当前线程,证明是锁重入操作,将 state + 1
                int nextc = c + acquires;
                // int 溢出了,可重入次数的上限
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                // 将 nextc 赋值给 state
                setState(nextc);
                // 锁重入成功
                return true;
            }
            // 获取锁失败
            return false;
        }

acquire()

ReentrantLock 的公平锁和非公平锁使用同一套 acquire() 逻辑。但是公平锁和非公平的tryAcquire(arg) 实现不同。

    public final void acquire(int arg) {
        // tryAcquire:查看当前线程是否可以尝试获取锁资源:1 其他线程刚好释放了锁 2 当前线程锁重入操作 
        if (!tryAcquire(arg) &&
            // 当前线程没有获取到锁资源
            // 先执行 addWaiter(Node.EXCLUSIVE) 将当前线程封装为 Node 结点,插入到 AQS 双向链表的末尾
            // acquireQueued 查看当前结点是否是第一个排队的结点,若是则再次尝试获取锁资源,若长时间拿不到则挂起线程
            // 若不是第一个排队的结点,则挂起线程
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            // 设置中断标记位,在 tryLock() 方法中会用到
            selfInterrupt();
    }

addWaiter()

    // 没竞争到锁资源的线程封装成 Node 进入双向链表排队
    // mode:Node.EXCLUSIVE 代表互斥锁
    private Node addWaiter(Node mode) {
        // 将当前线程封装为 Node 结点
        Node node = new Node(Thread.currentThread(), mode);
        // pred 指向尾结点
        Node pred = tail;
        // pred 不为空,代表链表中已经有结点在排队了
        if (pred != null) {
            // 分为三步将新结点挂到链表末端
            // 1. 当前结点的 prev 指向尾结点
            node.prev = pred;
            // 2. 以 CAS 的方式,将 tail 指向当前结点
            if (compareAndSetTail(pred, node)) {
                // 将 pred 的 next 指向当前结点
                pred.next = node;
                // 返回当前结点
                return node;
            }
        }
        // 若 CAS 失败或者 pred 为 null,则以死循环的方式,保证当前线程挂到双向链表末尾
        enq(node);
        return node;
    }

addWaiter 过程

enq()
    private Node enq(final Node node) {
        for (;;) {
            // 再次拿到尾节点
            Node t = tail;
            // 如果尾节点为空,则构建一个伪头节点作为 head 和 tail。
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                // 同 addWaiter 中的逻辑
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

acquireQueued()

判断当前线程是否还能再次尝试获取锁资源,如果不能再次获取,或者尝试获取但没获取到,则将当前线程挂起。

// 当前线程没有获取到锁资源,并且到AQS排队之后
// lock() 方法不用考虑中断。tryLock() 和 lockinterruptibly() 方法中才需要考虑。
final boolean acquireQueued(final Node node, int arg) {
        // 是否获取锁失败,true:失败 false:成功
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                // 获取当前结点的上一个结点
                final Node p = node.predecessor();
                // 若前继结点是伪头结点,则尝试获取锁
                if (p == head && tryAcquire(arg)) {
                    // 获取锁资源成功
                    // 将当前结点作为伪头结点,将 Node 的 thread 属性设置为 null
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 没资格竞争锁,或者有资格但是没竞争到锁
                // 基于当前结点的前继结点来判断当前结点是否可以挂起
                if (shouldParkAfterFailedAcquire(p, node) &&
                    // 基于 UNSAFE.park() 将当前线程挂起,等待被唤醒
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    } 
shouldParkAfterFailedAcquire()
// 是否可以挂起当前线程
// 已知:当前结点需要前继结点来唤醒它
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        // 表示 pred 结点的后续结点需要唤醒(-1)
        if (ws == Node.SIGNAL)
            return true;
        // pred 结点是取消状态(1)
        if (ws > 0) {
            // 往前找,直到找到一个结点的状态不等于 1 的结点。
            do {
            // 作为当前结点的上一个结点
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            // 将 pred 的 next 指向当前结点。
            pred.next = node;
        } else {
            // pred 结点的状态不是 1 和 -1(0、-2、-3),此处不涉及 -2 和 -3。所以状态是 0。
            // 将 pred 结点的状态改为 -1,下次循环时进入`if (ws == Node.SIGNAL)`判断
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
Node 的状态
static final class Node {
        /** Marker to indicate a node is waiting in shared mode */
        static final Node SHARED = new Node();
        /** Marker to indicate a node is waiting in exclusive mode */
        static final Node EXCLUSIVE = null;

        /** waitStatus value to indicate thread has cancelled */
        static final int CANCELLED =  1;
        /** waitStatus value to indicate successor's thread needs unparking */
        static final int SIGNAL    = -1;
        /** waitStatus value to indicate thread is waiting on condition */
        static final int CONDITION = -2;
        /**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate
         */
        static final int PROPAGATE = -3

        // ...
}

tryLock()

没有参数的 tryLock() 方法不区分公平和非公平锁。

public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

tryLock(long timeout, TimeUnit unit)

public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

tryAcquireNanos()

public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
        // 若线程的中断标记位被修改为 true,则抛异常
        // acquireQueued() 方法中有修改 interrupted 属性。
        if (Thread.interrupted())
            throw new InterruptedException();
        // 若 tryAcquire 获取锁成功,直接返回 true
        return tryAcquire(arg) ||
            doAcquireNanos(arg, nanosTimeout);
    }
doAcquireNanos()
private boolean doAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (nanosTimeout <= 0L)
            return false;
        final long deadline = System.nanoTime() + nanosTimeout;
        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 true;
                }
                // 计算竞争锁剩余的时间
                nanosTimeout = deadline - System.nanoTime();
                if (nanosTimeout <= 0L)
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) &&
                    // 若剩余时间大于1000纳秒则挂起
                    nanosTimeout > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);
                    // 若线程被唤醒,则判断是中断唤醒还是时间到了唤醒
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                // Node 在排队过程中被中断了
                cancelAcquire(node);
        }
    }
cancelAcquire()

cancelAcquire() 可以将当前结点从 AQS 链表中删除。主要流程如下:

  1. 将当前结点的线程设置为 null
  2. 往前找到有效结点 pred
  3. 将当前结点的状态设置为 1,代表取消
  4. 将当前结点脱离 AQS 队列,分为 3 种情况:当前结点是尾结点/不是伪头结点的后继结点/伪头结点的后继结点
private void cancelAcquire(Node node) {
        if (node == null)
            return;
		// 1. 线程设置为 null
        node.thread = null;
		// 2. 往前找有效结点并赋值给 pred
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;
				
		// 3. 拿到 pred 的 next
        Node predNext = pred.next;
				
		// 4. 当前结点的状态设置为取消
        node.waitStatus = Node.CANCELLED;
				
		// 若当前 node 是尾结点,将尾结点指向 pred
        if (node == tail && compareAndSetTail(node, pred)) {
			// 将 pred 的 next 设置为 null
            compareAndSetNext(pred, predNext, null);
        } else {
			// 当前结点不是尾结点或者 compareAndSetTail 失败
            int ws;
			// 当前结点不是伪头结点的后继结点
            if (pred != head &&
				// pred 结点的状态不为 -1 且不是取消状态(1),则通过 CAS 操作将 pred 的状态修改为 -1
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
				// 校验 pred 结点的线程,避免并发情况下,pred 变成取消结点或者头结点
                pred.thread != null) {
				// 进入条件判断说明 pred 是有效结点,可以唤醒后面的结点
                Node next = node.next;
				// node 的 next 不为 null,且不是取消结点,则将 pred 的 next 指向当前结点的 next
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
				// pred == head,当前结点是伪头结点的后继结点
				// 唤醒后面的结点
                unparkSuccessor(node);
            }
						
			// 当前结点的 next 指向当前结点
            node.next = node; // help GC
        }
    }

lockInterruptibly()

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

acquireInterruptibly(int arg)

public final void acquireInterruptibly(int arg)  
        throws InterruptedException {  
    if (Thread.interrupted())  
        throw new InterruptedException();  
    if (!tryAcquire(arg))  
        doAcquireInterruptibly(arg);  
}
doAcquireInterruptibly(int arg)
// 拿不到锁资源就一直等,直到锁资源释放后被唤醒或者被中断唤醒
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);  
    }
parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {  
    LockSupport.park(this);  
	// 可以确认当前挂起的线程是被中断唤醒的还是被正常唤醒的
	// 被中断唤醒返回 true,正常唤醒返回 false
    return Thread.interrupted();  
}

释放锁流程

释放锁不区分公平锁和非公平锁。

unlock()

public void unlock() {  
    sync.release(1);  
}

release(int arg)

public final boolean release(int arg) {
    if (tryRelease(arg)) {  
        Node h = head;  
        // 若 head 的状态不为 0(说明 head 的状态等于 -1,head 只有 0 和 -1 两种状态)
        // 说明 AQS 队列中有排队的线程,且线程已经挂起。
        if (h != null && h.waitStatus != 0)  
	        // 唤醒排队线程
            unparkSuccessor(h);  
        return true;  
    }  
    return false;  
}
tryRelease(int releases)
        protected final boolean tryRelease(int releases) {
	        // state - 1
            int c = getState() - releases;
            // 判断当前持有锁的线程是不是当前线程
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            // 当前锁资源是否完全释放了(主要是因为可重入,重入几次就要释放几次)
            boolean free = false;
            if (c == 0) {
		        // 若 state - 1 == 0
                free = true;
                // 将持有锁的线程置为 null
                setExclusiveOwnerThread(null);
            }
            // 将 state 更新为 c
            setState(c);
            // 若锁资源完全释放了,返回 true;否则返回 false。
            return free;
        }
unparkSuccessor(Node node)

从后往前找离 head 最近的有效节点并唤醒。

    private void unparkSuccessor(Node node) {
	    // 拿到头结点的状态
        int ws = node.waitStatus;
        // 头结点小于 0 的情况只有 -1
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0); // 先基于 CAS 操作,将头结点的状态从 -1 改为 0

		// 头结点的后继结点
        Node s = node.next;
        // 若后继结点取消了(s.waitStatus > 0 就是 1)
        if (s == null || s.waitStatus > 0) {
            s = null;
            // 从后往前找到一个与 head 最近的有效结点,可以避免跳过某个新加入的结点。(从后往前找的原因可以看设置 tail 节点的流程)
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
		    // 唤醒结点
            LockSupport.unpark(s.thread);
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值