ReentrantLock分析

ReentrantLock是Java中可重入的互斥锁,性能接近synchronized,并通过AQS(AbstractQueuedSynchronizer)实现。它支持公平和非公平模式,非公平模式下直接尝试CAS获取锁,失败则进入AQS同步队列;公平模式下会检查是否有线程在等待。加锁和解锁过程涉及CAS操作、线程的挂起与唤醒,以及锁的重入计数。tryAcquire方法用于尝试获取锁,tryRelease用于释放锁,释放后可能唤醒后续等待的线程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ReentrantLock分析

1,ReentrantLock为可重入互斥锁,性能上与JDK1.6后的synchronized差不多,后者为隐士加锁,并且在JDK1.6引入了锁升级概念,由偏向锁升级为轻量级锁,最后升级为重量级锁,且不能锁降级
2,synchronized是JVM层面的实现,C++代码,比如重量锁基于ObejctMoniter,支持非公平。ReentrantLock基于AQS实现,这些实现逻辑都在sync中,ReentrantLock本身实现了Lock接口,支持公平和非公平模式,构造方法中通过参数fair设置
3,ReentrantLock没有对ConditionObject做额外的实现,直接用AQS的



前言

1,state是CAS中内置的volatile变量,为0时则没有线程获取到锁资源
2,默认非公平锁,外部线程与AQS同步队列中被唤醒的线程都抢占锁。公平锁则都从队列中排队获取锁资源
3,AQS内置的同步队列是双向链表。ConditionObject则是单向链表
4,很多核心逻辑ReentrantLock没有重写,直接用的AQS的


以下是本篇文章正文内容,下面案例仅供参考

一、分析加锁流程

1,分析lock方法

    public void lock() {
    	// 有公平实现(FairSync)和非公平实现(NonFairSync)
        sync.lock();
    }
    
	// 非公平实现
    final void lock() {
    	// 使用CAS,将state从0修改为1
        if (compareAndSetState(0, 1))
        	// 获取到了锁资源,将当前线程赋值给exclusiveOwnerThread变量(表示谁拿到了锁资源)
            setExclusiveOwnerThread(Thread.currentThread());
        else
        	// 获取锁失败,执行acquire获取锁
            acquire(1);
    }
    
    // 公平实现
    final void lock() {
    	// 获取锁,如果获取失败,进入AQS同步队列的尾部
        acquire(1);
    }

获取锁的过程,就是CAS state的过程,并且设置了变量来保存当前是哪个线程拿到了锁资源。
非公平模式获取锁:直接CAS状态(获取锁),CAS失败
执行acquire方法,如果锁没有被占用再次尝试CAS,如果锁被占用则判断是否是当前线程占用,如果是修改锁重入次数。没有获取到锁时会进入AQS同步队列中,执行LockSpport.part进入休眠状态

公平模式获取锁:执行acquire,步骤和非公平锁只有一处不一样:锁未被占用时会判断是否轮到自己获取锁(前面是否有排队的),前面没有排队的才获取

2,分析acquire方法

公平锁和非公平锁都调用了该方法

    public final void acquire(int arg) {
    	// 尝试获取锁资源
        if (!tryAcquire(arg) &&
        	// 仍然没有拿到锁资源
      		// addWaiter: 将当前包装成Node,追加AQS同步队列尾部(EXCLUSIVE表示排他模式)
      		// acquireQueued: 如果当前节点是head,再次尝试获取锁,如果仍然拿不到,线程会被挂起(WAITING)
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            // Thread.currentThread().interrupt() 设置中断标志位(只是设置,不一定会终止)
            selfInterrupt();
    }
    // 将没有获取到锁的线程添加到队列尾部
    private Node addWaiter(Node mode) {
    	// 将当前线程封装成Node
        Node node = new Node(Thread.currentThread(), mode);
        // 尾节点tail为当前节点的pred
        Node pred = tail;
        // 如果尾节点不为null,说明队列已经有排队的Node
        if (pred != null) {
            node.prev = pred;
            // 将当前节点设置tail节点(尾部追加)
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        // 尾节点为空或者compareAndSetTail CAS失败,在这里死循环执行
        // tail为null则死循环执行compareAndSetHead
        // 反之,死循环执行compareAndSetTail
        enq(node);
        return node;
    }
    // tryAcquire获取锁失败后,将线程放入队列尾部,然后执行该方法
    final boolean acquireQueued(final Node node, int arg) {
    	// 获取锁是否失败
        boolean failed = true;
        try {
            boolean interrupted = false;
            // 死循环
            for (;;) {
            	// 获取当前节点的上一个节点,没有就抛异常
                final Node p = node.predecessor();
                // 如果上一个节点是head,再次尝试获取锁
                // head内部的thread为空,这是一个伪节点,所以此处当前节点是第一个等待获取锁资源的线程
                if (p == head && tryAcquire(arg)) {
                	// 获取锁资源成功,将当前节点指向head,通知置空内部的thread和pre
                	// 相当于将原来的head脱离出队列,等待被回收
                    setHead(node);
                    p.next = null; // help GC
                    // 获取锁未失败
                    failed = false;
                    // 出口
                    return interrupted;
                }
                // 当前节点不是第二个节点 || 获取锁又失败了
                // 判断当前线程是否可以挂起(LockSupport.park(this),进入WAITING)
                if (shouldParkAfterFailedAcquire(p, node) &&
                	// 执行LockSupport.park(this),挂起线程
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
            	// 取消节点的处理
                cancelAcquire(node);
        }
    }
    // 判断node节点是否可以被挂起
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        // 如果上一个节点的状态是-1,则该节点还在排队中
        if (ws == Node.SIGNAL)
            return true;
        // 如果上一个节点状态是1(相当于该节点被取消,这样的节点是无效节点会从队列中移除)
        if (ws > 0) {
            do {
            	// 当前节点的上一个 = 上一个节点的上一个,相当于指针绕过了中间的节点
                node.prev = pred = pred.prev;
            // 一直循环,直到找到有效的pre节点(状态不为1)
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
			// 节点状态不是-1,也不是1,将pred 节点状态设置成-1
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        // 不能挂起
        return false;
    }

3,分析tryAcquire方法

公平实现和非公平实现方法不一样,但大致逻辑差不多,公平只是多加了公平获取锁方面的校验

	// 非公平实现
    final boolean nonfairTryAcquire(int acquires) {
    	// 获取当前线程
        final Thread current = Thread.currentThread();
        // 获取当前锁状态
        int c = getState();
        // 锁状态为0,说明没有被占用,可能已经被其他线程释放了
        if (c == 0) {
        	// CAS状态,获取锁
            if (compareAndSetState(0, acquires)) {
            	// 获取锁完成,设置ExclusiveOwnerThread
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        // 锁被占用了,判断是否是当前线程占用的(ReentrantLock是可重入锁)
        else if (current == getExclusiveOwnerThread()) {
        	// 增加锁的重入次数
            int nextc = c + acquires;
            // 重入次数不能超过int的边界
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
             // 设置锁状态
            setState(nextc);
            return true;
        }
        return false;
    }
    
    // 公平实现
    protected final boolean tryAcquire(int acquires) {
   		// 获取当前线程
        final Thread current = Thread.currentThread();
        // 获取当前锁状态
        int c = getState();
        // 锁状态为0,说明没有被占用,可能已经被其他线程释放了
        if (c == 0) {
        	// 判断是否有线程排在里面当前线程的前面,如果没有,才会去获取锁
            if (!hasQueuedPredecessors() &&
            		// 获取锁
                    compareAndSetState(0, acquires)) {
                // 获取锁成功
                setExclusiveOwnerThread(current);
                return true;
            }
        } else if (current == getExclusiveOwnerThread()) {
        	// 增加锁重入次数
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
    // 公平锁:判断当前线程是否可以获取锁,返回false说明前面没有线程排队
   public final boolean hasQueuedPredecessors() {
   		// 尾节点
        Node t = tail; 
        // 头节点
        Node h = head;
        // 头节点的下一个节点
        Node s;
        // 头 != 尾
        return h != t &&
        	// 头的下一节点不为null || 头的下一节点不是当前线程(说明前面有排队的)
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

4,分析tryLock方法

与上面的逻辑基本一致

    public boolean tryLock() {
    	// 不区分公平模式还是非公平模式,都会走非公平逻辑
        return sync.nonfairTryAcquire(1);
    }
    // 非公平模式获取锁
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        // 锁没有被占用,CAS获取锁
        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;
    }
    // 待有时间的获取锁
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        // 将等待时间转成纳秒
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
    public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
       	// 线程被打断时抛出异常
        if (Thread.interrupted())
            throw new InterruptedException();
        // 尝试获取锁
        return tryAcquire(arg) ||
        	// 没有获取到时,执行带有等待时间的“tryLock”
            doAcquireNanos(arg, nanosTimeout);
    }
   // 带有等待时间的获取锁
    private boolean doAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        // 没有等待时间,获取锁失败 
        if (nanosTimeout <= 0L)
            return false;
        // 计算锁的结束时间
        final long deadline = System.nanoTime() + nanosTimeout;
        // 将当前线程线程封装成Node,追加到队列尾部,并返回当前Node
        final Node node = addWaiter(Node.EXCLUSIVE);
        // 是否获取锁失败
        boolean failed = true;
        try {
        	// 死循环
            for (;;) {
            	// 获取当前node的上一个节点
                final Node p = node.predecessor();
                // 上一节点是head则到了自己获取锁的后,如果获取成功,将当前node设置为head,旧head等待回收
                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;
                // 判断是否可以挂起线程(WAITING)
                if (shouldParkAfterFailedAcquire(p, node) &&
                	// 可以挂起,判断剩余时间是否大于1000纳秒,给LockSupport.parkNanos留出一定的执行时间
                    nanosTimeout > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);
                // 线程被WAITING时,可以通过设置中断标志位唤醒,这种方式直接抛出异常
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
            	// 取消node的处理
                cancelAcquire(node);
        }
    }

lockInterruptibly获取锁与上面类似,不在分析,区别在于获取锁时,获取不到则一直获取,除非设置了中断标志位

二、分析释放锁流程

1.分析unlock方法

代码如下(示例):

   // 释放锁,不区分公平模式和非公平模式
   public void unlock() {
        sync.release(1);
    }

	public final boolean release(int arg) {
		// 尝试释放锁,如果重入次数递减后不为0,说明仍然持有锁资源,此时为false
        if (tryRelease(arg)) {
        	// 获取head
            Node h = head;
            if (h != null && h.waitStatus != 0)
            	// 唤醒后面的线程
                unparkSuccessor(h);
            return true;
        }
        return false;
	}
	// 尝试释放锁
	protected final boolean tryRelease(int releases) {
		// 获取锁的重入次数
        int c = getState() - releases;
        // 当前没有持有锁资源吗,抛出异常
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        // 锁是否被完全释放
        boolean free = false;
        // 重入次数等于0,说明全部释放了
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
    
	private void unparkSuccessor(Node node) {
        // 获取node节点的状态
        int ws = node.waitStatus;
        if (ws < 0)
        	// 如果节点转状态为-1,设置为0(初始化状态)
            compareAndSetWaitStatus(node, ws, 0);

        Node s = node.next;
        // 如果没有下一节点 || 或者节点状态为1(意味着被设置为取消)
        if (s == null || s.waitStatus > 0) {
            s = null;
            // 从尾部遍历,找到正常的节点
            // 因为新增节点时,指针会先指向链表的尾部,关系还没有建立完整,无法正序找到这个刚刚建立的node
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
        	// 唤醒线程
            LockSupport.unpark(s.thread);
    }

总结

待总结。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值