ReentrantLock 是可重入锁
啥是可重入锁呢?比如:线程 1 通过调用 lock() 方法获取锁之后,再调用 lock 时,就不会再进行阻塞获取锁,而是直接增加重试次数。
还记得 synchronized 吗?它有 monitorenter 和 monitorexit 两种指令来保证锁,而它们的作用可以理解为每个锁对象拥有一个锁计数器,也就是如果再次调用 lock() 方法,计数器会进行加 1 操作
所以, synchronized 和 ReentrantLock 都是可重入锁
ReentrantLock 与 synchronized 区别
既然 synchronized 和 ReentrantLock 都是可重入锁,那 ReentrantLock 与 synchronized 有什么区别呢?
synchronized 是 Java 语言层面提供的语法,所以不需要考虑异常;ReentrantLock 是 Java 代码实现的锁,所以必须先要获取锁,然后再正确释放锁
synchronized 在获取锁时必须一直等待没有额外的尝试机制;ReentrantLock 可以尝试获取锁(这一点等下分析源码时会看到)
ReentrantLock 支持获取锁时的公平和非公平选择
不 BB 了,直接上源码
lock & NonfairSync & FairSync 详解
lock
锁的入口是 lock() 方法:
public void lock() { sync.lock(); }
其中, sync 是 ReentrantLock 的静态内部类,它继承 AQS 来实现重入锁的逻辑, Sync 有两个具体实现类: NonfairSync 和 FairSync
NonfairSync
先来看一下 NonfairSync :
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ // 重写 Sync 的 lock 方法 final void lock() { // 先不管其他,上来就先 CAS 操作,尝试抢占一下锁 if (compareAndSetState(0, 1)) // 如果抢占成功,就获得了锁 setExclusiveOwnerThread(Thread.currentThread()); else // 没有抢占成功,调用 acquire() 方法,走里面的逻辑 acquire(1); } // 重写了 AQS 的 tryAcquire 方法 protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
FairSync
接下来看一下 FairSync :
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; // 重写 Sync 的 lock 方法 final void lock() { acquire(1); } /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ // 重写了 Sync 的 tryAcquire 方法 protected final boolean tryAcquire(int acquires) { // 获取当前执行的线程 final Thread current = Thread.currentThread(); // 获取 state 的值 int c = getState(); // 在无锁状态下 if (c == 0) { // 没有前驱节点且替换 state 的值成功时 if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { // 保存当前获得锁的线程,下次再来时,就不需要尝试竞争锁,直接重入即可 setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { // 如果是同一个线程来获得锁,直接增加重入次数即可 int nextc = c + acquires; // nextc 小于 0 ,抛异常 if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); // 获取锁成功 return true; } // 获取锁失败 return false; } }
总结 NonfairSync 与 FairSync
到这里,应该就比较清楚了, Sync 有两个具体的实现类,分别是:
- NonfairSync :可以抢占锁,调用 NonfairSync 时,不管当前队列上有没有其他线程在等待,上来我就先 CAS 操作一番,成功了就获得了锁,没有成功就走 acquire 的逻辑;在释放锁资源时,走的是 Sync.nonfairTryAcquire 方法
- FairSync :所有线程按照 FIFO 来获取锁,在 lock 方法中,没有 CAS 尝试,直接就是 acquire 的逻辑;在释放资源时,走的是自己的 tryAcquire 逻辑
接下来咱们看看 NonfairSync 和 FairSync 是如何获取锁的