简单介绍
ReentrantLock
是一个可重入的独占锁
-
可重入
同一线程外层函数获得锁之后,内层递归函数仍然可以获取该锁的代码
该特性带来的两个问题:- 如何识别获取锁的线程是否为当前占据锁的线程
- 线程重复 n 次获取了锁,需要释放 n 次锁,否则会导致别的线程无法获得锁
-
独占
一次只能被一个线程所持有
类型
private final Sync sync;
ReentrantLock
的内部类 Sync
继承了 AQS
(AbstractQueuedSynchronizer
),并且有公平锁 FairSync
和 非公平锁 NonfaireSync
两个字类,ReentrantLock
的获取与释放锁操作都是委托给该同步组件来实现的
(注:该篇文章里所有 AQS
相关的内容会再写一篇相关的文章,这里不详细介绍)
- 公平锁
是指当锁可用时,在锁上等待时间最长的线程将获取锁的使用权(先来先得)
使用有参构造方法,传入true
创建公平锁public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
- 非公平锁
随机分配使用权,使用ReentrantLock
无参的构造函数,默认创建的是非公平锁public ReentrantLock() { sync = new NonfairSync(); }
常用方法介绍
-
lock()
方法获取锁,如果获取不到锁,则当前线程在获取到锁之前都不可调度(不响应中断)public void lock() { sync.lock(); }
-
lockInterruptibly()
方法获取锁,则当前线程在获取到锁之前都不可调度,除非有其他线程中断了当前线程(响应中断)public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); }
-
tryLock()
方法获取锁,如果调用的时候能够获取锁,那么就获取锁并且返回true
,如果当前的锁无法获取到,那么这个方法会立刻返回false
public boolean tryLock() { return sync.nonfairTryAcquire(1); }
-
tryLock(long timeout, TimeUnit unit)
方法获取锁,在指定时间内尝试获取锁。如果可以获取锁,则获取锁并返回true
,如果无法获取锁,则当前线程变为不可调度,除非当前线程被中断或者到了指定的等待时间public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); }
-
unlock()
释放锁,释放当前线程占用的锁。注意:获取了几次锁,就要释放几次锁public void unlock() { sync.release(1); }
-
newCondition()
方法,返回一个与当前的锁关联的条件变量。在使用这个条件变量之前,当前线程必须占用锁。调用Condition
的await
方法,会在等待之前原子地释放锁,并在等待被唤醒后原子的获取锁public Condition newCondition() { return sync.newCondition(); }
-
isHeldByCurrentThread()
方法,查询当前线程是否保持锁定public boolean isHeldByCurrentThread() { return sync.isHeldExclusively(); }
-
isLocked()
方法,查询该锁是否已经被锁定public boolean isLocked() { return sync.isLocked(); }
-
isFair()
方法,判断锁是公平锁还是非公平锁public final boolean isFair() { return sync instanceof FairSync; }
Sync 内部类
abstract static class Sync extends AbstractQueuedSynchronizer {
...
}
Sync
是一个抽象类型,它继承 AbstractQueuedSynchronizer
,这个 AbstractQueuedSynchronizer
是一个模板类,它实现了许多和锁相关的功能,并提供了钩子方法供用户实现,比如 tryAcquire
,tryRelease
等
static final class NonfairSync extends Sync {
final void lock() {
...
}
}
static final class FairSync extends Sync {
final void lock() {
...
}
}
NonfairSync
和 FairSync
两个类继承自 Sync
,实现了 lock
方法
-
lock
当我们调用ReentrantLock
的lock
方法的时候,实际上是调用了NonfairSync
或者FairSync
的lock
方法-
NonfairSync
非公平锁的lock
实现final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
CAS
操作,去尝试抢占该锁。如果成功,就把当前线程设置在这个锁上,表示抢占成功。如果失败,则调用acquire
模板方法,等待抢占
acquire
方法里调用了tryAcquire(int arg)
方法,NonfairSync
的tryAcquire
实际上又调用的Sync
的nonfairTryAcquire
方法,如下final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 判断锁的状态是不是 0,如果是,则尝试去原子抢占这个锁 if (compareAndSetState(0, acquires)) { // 如果抢占到了,把状态设置为1 setExclusiveOwnerThread(current); // 并且设置当前线程为独占线程 return true; } } else if (current == getExclusiveOwnerThread()) {// 如果锁的状态不为 0,判断该线程是否是独占线程(可重入) int nextc = c + acquires; // 如果当前线程是独占线程,则增加状态变量的值 if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); // 给状态变量赋值 return true; } return false; }
tryAcquire
一旦返回false
,就会则进入acquireQueued
流程,此段代码中锁的获取可以分为两种情况:-
state为0时:代表锁已经释放,可以去获取,所以使用
CAS
去获取锁,如果获取成功,则代表竞争锁成功,调用setExclusiveOwnerThread
设置当前线程为独占线程,因为队列中的线程和新线程都可以CAS
获取锁不需要排队,所以是非公平锁 -
status不为0时:代表锁已经被占有,如果当前线程是占有锁的线程(
current == getExclusiveOwnerThread()
为true
),更新state,意味着当前线程又一次的获取了锁,这就是可重入。
-
FairSync
公平锁的lock
实现final void lock() { acquire(1); }
acquire
方法里同样调用了tryAcquire(int arg)
方法,FairSync
的tryAcquire
方法实现如下:protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 判断锁的状态是不是 0,如果是 0,再判断是否有线程在排队获取锁 if (!hasQueuedPredecessors() && // 如果没有线程在排队获取锁则尝试原子抢占锁 compareAndSetState(0, acquires)) { // 如果抢占到了,把状态设置为1 setExclusiveOwnerThread(current); // 并且设置当前线程为独占线程 return true; } } else if (current == getExclusiveOwnerThread()) { // 如果锁的状态不为 0,判断该线程是否是独占线程(可重入) int nextc = c + acquires; // 如果当前线程是独占线程,则增加状态变量的值 if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); // 给状态变量赋值 return true; } return false; }
tryAcquire
一旦返回false
,就会则进入acquireQueued
流程,公平锁获取锁的过程与非公平锁不一样的地方在 state为0时 新线程需要判断有没有线程在排队获取锁,只有当没有的时候才会去尝试抢占锁,如果有线程在排队,新线程也会被加入到排队的队列中去 -
-
unlock
unlock
方法,其实是直接调用AbstractQueuedSynchronizer
的release
操作,release
方法先调用了tryRelease
方法,Sync
的tryRelease
方法实现如下:
一旦protected final boolean tryRelease(int releases) { int c = getState() - releases; //状态变量值减少,这里是考虑到可重入锁可能自身会多次占用锁 if (Thread.currentThread() != getExclusiveOwnerThread()) // 如果当前线程不是独占线程则抛异常 throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { // 当状态值为 0 ,锁释放 free = true; setExclusiveOwnerThread(null); // 将独占线程设置为 null } setState(c); // 状态变量赋值 return free; }
tryRelease
成功,下一个节点的线程被唤醒,被唤醒的线程就会进入acquireQueued
流程中,去获取锁