概述
ReentrantLock 可重入锁:ReentrantLock锁在同一个时间点只能被一个线程锁持有;而可重入的意思是,ReentrantLock锁,可以被单个线程多次获取。
类图
实现原理
1、属性
private final ReentrantLock.Sync sync;
/**
* 非公平锁
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
/**
* 公平锁
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
ReentrantLock的实现有公平锁(FairSync)和非公平锁(NonfairSync)2种。
默认是非公平锁。
公平锁:
在“公平锁”的机制下,线程依次排队获取锁。如果同时2个线程争夺锁资源, 第一个线程获取到了锁,第二个线程就会被加入到队列中进行等待。
非公平锁:
主要是通过CAS算法进行随机匹配,没有队列的概念。但是如果通过CAS计算还是没有获取到锁的话,就会继续进行尝试获取锁。
2、构造器
public ReentrantLock() {
this.sync = new ReentrantLock.NonfairSync();
}
public ReentrantLock(boolean var1) {
this.sync = (ReentrantLock.Sync)(var1 ? new ReentrantLock.FairSync() : new ReentrantLock.NonfairSync());
}
默认是创建一个非公平锁。
3、核心方法
// 获取锁,给当前线程加锁
public void lock() {
this.sync.lock();
}
// 如果当前线程未被中断,则获取锁定。如果已经被中断,则抛出异常。
public void lockInterruptibly() throws InterruptedException {
this.sync.acquireInterruptibly(1);
}
// 尝试去获取锁
public boolean tryLock() {
return this.sync.nonfairTryAcquire(1);
}
// 尝试在固定的时间去获取锁
public boolean tryLock(long var1, TimeUnit var3) throws InterruptedException {
return this.sync.tryAcquireNanos(1, var3.toNanos(var1));
}
// 释放锁
public void unlock() {
this.sync.release(1);
}
// 创建对象监视器。
public Condition newCondition() {
return this.sync.newCondition();
}
// 查询当前线程保持锁定的个数
public int getHoldCount() {
return this.sync.getHoldCount();
}
// 判断当前线程是否保持锁定状态
public boolean isHeldByCurrentThread() {
return this.sync.isHeldExclusively();
}
// 查询此锁是否由任意线程保持
public boolean isLocked() {
return this.sync.isLocked();
}
// 判断锁的类型是否是公平锁
public final boolean isFair() {
return this.sync instanceof ReentrantLock.FairSync;
}
// 查询队列中是否存在等待获取锁的线程数
public final boolean hasQueuedThreads() {
return this.sync.hasQueuedThreads();
}
// 查询队列中是否存在指定等待获取锁的线程
public final boolean hasQueuedThread(Thread var1) {
return this.sync.isQueued(var1);
}
// 等待获取此锁的线程的估计数
public final int getQueueLength() {
return this.sync.getQueueLength();
}
// 查询队列中所有的线程数
protected Collection<Thread> getQueuedThreads() {
return this.sync.getQueuedThreads();
}
不同点
非公平锁和公平锁的两处不同
非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁返回了。
非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法,在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,但是公平锁会判断等待队列是否有线程处于等待状态,如果有则不去抢锁,乖乖排到后面。
公平锁和非公平锁就这两点区别,如果这两次 CAS 都不成功,那么后面非公平锁和公平锁是一样的,都要进入到阻塞队列等待唤醒。