ReentrantLock的基本介绍
前言
ReentrantLock很多读者都了解过,笔者亦曾听过,作为了并发中与synchronize关键字对齐的同步安全保障,笔者认为还是有必要去深入了解一下。
正文
要了解ReentrantLock,就得知道AQS,lock实现的核心机制就是基于aqs框架,读者可自行查阅其他资料,或观看笔者关于aqs的介绍
常量
首先来观察一下类的常量
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
ReentrantLock实现了lock接口和Serializable序列化接口,常量的话一个是序列化id,一个是内部类的对象sync。观察一下这个内部类
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
观察到这个内部类了继承了AbstractQueuedSynchronizer简写AQS这个类。
构造方法
ReentrantLock一共有两个构造方法
// 默认构造方法,创建非公平的同步锁
public ReentrantLock() {
sync = new NonfairSync();
}
// 含参构造,可创建非公平还是公平的锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
NonfairSync和FairSync这两方法都是ReentrantLock内部类的默认构造方法,ReentrantLock一共有三个内部类,之前介绍过Sync内部类,现在看下剩下的两个内部类
/**
* Sync object for non-fair locks
*/
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() {
// 运用到cas理论,将状态值置为1
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
// 覆写AQS父类的tryAcquire方法
protected final boolean tryAcquire(int acquires) {
// 调用非公平尝试竞争锁方法
return nonfairTryAcquire(acquires);
}
}
/**
* Sync object for fair locks
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
// 调用父类AQS的独占锁获取方法
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
// 覆写父类AQS的获取方法
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 父类AQS的方法,获取状态
int c = getState();
if (c == 0) {
// AQS的队列是否有前驱节点
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中调用了很多父类AQS中的方法,所以读者在研究ReentrantLock时,优先研究AQS的代码。
tryAcquire方法剖析
读者可能好奇为何不去剖析lock方法中的acquire方法,而来剖析tryAcquire方法,因为acquire方法是调用父类AQS的acquire方法,笔者在AQS的文章中详细介绍了独占锁和共享锁的获取去释放,本文详细介绍一下AQS中一些方法在子类的覆写。
*/
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取资源状态
int c = getState();
if (c == 0) {
// cas成功,说明获取到锁,调用父类的setExclusiveOwnerThread方法
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 如果当前线程已经获取到锁,就更新state的值
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;
}
tryRelease方法剖析
protected final boolean tryRelease(int releases) {
// 获取当前状态值减去入参releases值(1)
int c = getState() - releases;
// 占有锁的线程不是当前线程报错
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 状态值为0说明释放成功
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
可能有读者好奇为何笔者能断定入参releases值为1。
因为在代码中参数值写死了
public void unlock() {
sync.release(1);
}
调用内部类的release方法,而sync内部类release方法是父类AQS的中final修饰的方法,禁止子类覆写
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
父类中会调用子类覆写的tryRelease方法,这也就是为何笔者敢断言入参值为1.
后话
如果了解AQS的话,发现ReentrantLock的代码理解简单,基于AQS的独占锁方式实现,覆写了父类的tryAcquire以及tryRelease方法,增减了公平锁以及非公平锁的机制。