ReentrantLock 源码解析(二)—— 加锁解锁方法概览

本文解析了ReentrantLock的加锁与解锁机制,重点介绍了非公平锁的实现原理,包括自旋、CAS及阻塞等核心概念。

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

承接上文,了解了AQS,现就来看看ReentrantLock 加解锁的逻辑。(不了解AQS的点击这里 -> AQS简述

park 与 unpark

ReentrantLock加锁逻辑

这里以非公平锁为例来说明,这里画了个简化的流程图,先有个印象

在这里插入图片描述

ReentrantLock这个类,并没有直接继承AQS,而在ReentrantLock有一个内部类,sync,它继承了AQS。

ReentrantLock lock = new ReentrantLock();
// 当执行这段代码时,就会创建一个AQS 对象,其status是0,具体原码如下

    public ReentrantLock() {
        sync = new NonfairSync();  // 默认是非公平锁
    }

lock.lock(); // 加锁

执行这行代码时,会涉及到 自旋、 CAS、 入队、 阻塞,下面照着原始说逻辑,具体先不展开。

// 非公平锁实现
     final void lock() {
         if (compareAndSetState(0, 1)) // 成功将state 由0改为1的线程,直接就可以拿到锁
             setExclusiveOwnerThread(Thread.currentThread());
         else
             acquire(1); // 没拿到锁,做特殊处理
     }

// 获取独占锁
    public final void acquire(int arg) {
        if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)){
        	selfInterrupt(); 
        }    
    }

acquire() 是获取独占锁的核心方法,咱先把整体逻辑讲完,然后再对原码进行逐行分析。

tryAcquire(arg)
尝试获取锁,成功返回true,否则返回false

addWaiter(Node.EXCLUSIVE)
自旋的方式入队,直到成功入队为止,否则重试

acquireQueued(final Node node, int arg)
再次尝试获取锁,若成功则返回,失败了就阻塞线程。其中阻塞线程是调用了 LockSupport.park() 方法

ReentrantLock解锁逻辑


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

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

tryRelease(arg)
修改 state 属性,state = 0 时,即解锁成功

unparkSuccessor(h)
唤醒头节点,使其去抢锁,其中解除阻塞,是调用了LockSupport.unpark()方法

用前文示例代码,来个第一阶段的总结

       
       lock.lock(); // 假设10个线程同时走到这行,只可能有一个线程拿到锁,继续往下走,其他线程阻塞,停在这一行。
       for(int j = 0; j < 1000; j++){
           total++;
       }
       lock.unlock(); // 执有锁的线程释放了锁,阻塞的线程有机会抢锁,
      				 // 抢锁成功的可以往下走,其余的继续阻塞。
       

在加锁解锁过程中,用了CAS,用了自旋,简要说下它俩啥意思,然后再开始讲原码。

CAS 可以保证,不论并发有多高,只可能有一个线程执行成功。


比如说,现在账户里有10块,现在要给账户上加5块钱,三个线程同时执行这个操作。

CAS要求传两个值过去 原来的值10,改动后的值15。底层执行的时候,先比较一下,当前值是不是10。

如果不是,就直接返回false,如果是10,那就将10改为15。

这里比较并修改是原子操作,底层语言保证这一点。

刚刚说的那三个线程,只有一个线程会修改账户的钱,并返回true,其它两个会返回失败,不修改账户的钱。

自旋 简而言之就是死循环,比如 while(true){},名字很高大上,其实就那么回事。

番外篇: park, unpark 不要傻傻分不清

上一篇: AQS简介

下一篇: 加锁源码详细剖析

至此,ReentrantLock加锁解锁,最基本的知识讲完了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值