ReentrantLock和Synchronized都是Java开发中最常用的锁,与Synchronized这种JVM内置锁不同的是,ReentrantLock提供了更丰富的语义。可以创建公平锁或非公平锁、响应中断、超时等待、按条件唤醒等。在某些场景下,使用ReentrantLock更适合,功能更强大。,前两篇文章,我们分析了AQS的加锁流程、以及源码实现。当时我们就说了,AQS使用了模板设计模式,父类中定义加锁流程,子类去实现具体的加锁逻辑。所以大部分加锁代码已经在父类AQS中实现了,导致ReentrantLock的源码非常简单,一块学习一下。,先看一下ReentrantLock怎么使用?,可以看到ReentrantLock的使用非常简单,调用lock加锁,unlock释放锁,需要配置try/finally使用,保证在代码执行出错的时候也能释放锁。,ReentrantLock也可以配合Condition条件使用,具体可以翻一下前几篇文章中BlockingQueue的源码解析,那里面有ReentrantLock的实际使用。,再看一下ReentrantLock的类结构,可以看出ReentrantLock的类结构非常简单,实现了Lock接口。,类里面有两个静态内部类,分别实现公平锁和非公平锁。,看一下Lock接口中,定义了哪些方法?,就是一些使用锁的常用方法。,在上篇文章中浏览AQS源码的时候,了解到AQS定义了一些有关具体加锁、释放锁的抽象方法,留给子类去实现,再看一下有哪些抽象方法:,由于ReentrantLock使用的是独占锁,所以只需要实现独占锁相关的方法就可以了。,3.1 ReentrantLock构造方法,在创建ReentrantLock对象的时候,可以指定使用公平锁还是非公平锁,默认使用非公平锁,显然非公平锁的性能更好。,先思考一个面试常考问题,公平锁和非公平锁是怎么实现的?,3.2 非公平锁源码,先看一下加锁源码:,从父类ReentrantLock的加锁方法入口:,在子类NonfairSync的加锁方法:,加锁逻辑也很简单,先尝试使用CAS加锁(也就是把state从0设置成1),加锁成功,就把当前线程设置为持有锁线程。,设计者很聪明,在锁竞争不激烈的情况下,很大概率可以加锁成功,也就不用走else中复杂的加锁逻辑了。,如果没有加锁成功,还是需要走else中调用父类AQS的acquire方法,而acquire又需要调用子类的tryAcquire方法。,调用链路就是下面这样:,
,根据调用链路,实际的加锁逻辑在Sync.nonfairTryAcquire方法里面。,再看一下释放锁的调用流程,公平锁和非公平锁流程是一样的,最终都是执行Sync.tryRelease方法:,
,再看一下公平锁的源码,3.3 公平锁源码,先看一下公平锁的加锁流程:,
,最终的加锁方法是FairSync.tryAcquire,看一下具体逻辑:,公平锁的释放锁逻辑跟非公平锁一样,上面已经讲过。,看完了ReentrantLock的所有源码,是不是觉得ReentrantLock很简单。,由于加锁流程的编排工作已经在父类AQS中实现,子类只需要实现具体的加锁逻辑即可。,加锁逻辑也很简单,也就是修改同步状态state的值和持有锁的线程exclusiveOwnerThread。
© 版权声明
文章版权归作者所有,未经允许请勿转载。