《Dubbo 实现原理与源码解析 —— 精品合集》《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》

摘要: 原创出处 http://cmsblogs.com/?p=2210 「小明哥」欢迎转载,保留摘要,谢谢!

作为「小明哥」的忠实读者,「老艿艿」略作修改,记录在理解过程中,参考的资料。


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

此篇博客所有源码均来自JDK 1.8

1. 简介

ReentrantLock,可重入锁,是一种递归无阻塞的同步机制。它可以等同于 synchronized 的使用,但是 ReentrantLock 提供了比 synchronized 更强大、灵活的锁机制,可以减少死锁发生的概率。

API 介绍如下:

一个可重入的互斥锁定 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义,但功能更强大。

ReentrantLock 将由最近成功获得锁定,并且还没有释放该锁定的线程所拥有。当锁定没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁定并返回。如果当前线程已经拥有该锁定,此方法将立即返回。可以使用 #isHeldByCurrentThread()#getHoldCount() 方法来检查此情况是否发生。

ReentrantLock 还提供了公平锁非公平锁的选择,通过构造方法接受一个可选的 fair 参数(默认非公平锁):当设置为 true 时,表示公平锁;否则为非公平锁。

公平锁与非公平锁的区别在于,公平锁的锁获取是有顺序的。但是公平锁的效率往往没有非公平锁的效率高,在许多线程访问的情况下,公平锁表现出较低的吞吐量。

ReentrantLock 整体结构如下图:

201702010001

  • ReentrantLock 实现 Lock 接口,基于内部的 Sync 实现。
  • Sync 实现 AQS ,提供了 FairSync 和 NonFairSync 两种实现。

2. Sync 抽象类

Sync 是 ReentrantLock 的内部静态类,实现 AbstractQueuedSynchronizer 抽象类,同步器抽象类。它使用 AQS 的 state 字段,来表示当前锁的持有数量,从而实现可重入的特性。

2.1 lock


abstract void lock();
  • 执行锁。抽象了该方法的原因是,允许子类实现快速获得非公平锁的逻辑。

2.2 nonfairTryAcquire

#nonfairTryAcquire(int acquires) 方法,非公平锁的方式获得锁。代码如下:

final boolean nonfairTryAcquire(int acquires) {
//当前线程
final Thread current = Thread.currentThread();
//获取同步状态
int c = getState();
//state == 0,表示没有该锁处于空闲状态
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;
}
  • 该方法主要逻辑:首先判断同步状态 state == 0 ?
    • 如果,表示该锁还没有被线程持有,直接通过CAS获取同步状态。
      • 如果成功,返回 true 。
      • 否则,返回 false 。
    • 如果不是,则判断当前线程是否为获取锁的线程
      • 如果是,则获取锁,成功返回 true 。成功获取锁的线程,再次获取锁,这是增加了同步状态 state 。通过这里的实现,我们可以看到上面提到的 “它使用 AQS 的 state 字段,来表示当前锁的持有数量,从而实现可重入的特性”。
      • 否则,返回 false 。
  • 理论来说,这个方法应该在子类 FairSync 中实现,但是为什么会在这里呢?在下文的 ReentrantLock.tryLock() 中,详细解析。