您的位置:首页 > 编程语言 > Java开发

java可重入锁ReentrantLock原理

2016-04-17 22:41 956 查看

ReentrantLock

JUC中ReentrantLock实现了独占模式重入锁,对于可重入,此类的注释是这样的:

当一个线程锁住ReentrantLock,但是没有解锁,这个线程再执行加锁方法会返回成功,并获得这把锁

ReentrantLock类图



由上图可知,ReentrantLock包含两个静态属性,一个是java 序列化Serializable使用的属性serialVersionUID,一个是sync,sync是内部类Sync的实例,ReentrantLock中还包含两个另外两个子类FairSync,NonfairSync。它们都继承自Sync类。FairSync是公平锁的实现方式,NonfairSync是非公平锁的实现方式。他们两都实现了可重入。

ReentrantLock初始化

ReentrantLock有两个不同的初始化方法

public ReentrantLock() {
//默认方式:设置sync属性为非公平模式锁NonfairSync实例
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
//根据用户传入是否公平参数,设置sync属性为非公平模式锁NonfairSync或者公平模式锁FairSync实例
sync = fair ? new FairSync() : new NonfairSync();
}


ReentrantLock加锁解锁

ReentrantLock解锁解锁执行的都是sync的方法

//加锁
public void lock() {
sync.lock();
}
//可中断加锁
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
//尝试加锁
public boolean tryLock() {
return sync.nonfairTryAcquire(1);//使用非公平模式获取一次锁
}
//带时间限制的尝试加锁
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
//释放锁
public void unlock() {
sync.release(1);
}


Sync加锁解锁

Sync的加锁方法是一个abstract 方法,交由子类实现

abstract void lock();


公平重入锁

其中公平重入锁方式直接调用aqs的acquire,然后自身实现了aqs acquire方法会调用的tryAcquire方法(aqs的流程详见上一篇aqs的介绍)。其中tryAcquire获取锁首先会判断当前state是否为0。

如果不为0则代表锁已被锁住,如果被锁住,则查看占用的线程是否为当前线程。如果是当前线程占用了这个锁,则可重入,进行state增加。state可以表示持有者重入次数。

如果为0,则判断当前线程代表的节点在队列中有没有在排队的前任,如果没有在排队的前任则尝试获取锁。因为新加入获取锁队列的节点都是加在队列的尾部。而只有队列中没有前任在排队获取锁的节点才能尝试获取锁,维持了一个先进先出的规则。所有线程都是根据进入顺序去持有锁的,是公平的方式。

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;
}
}
//如果持有锁的为自己,则重入state+=acquires
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}


非公平重入锁

非公平重入锁NonfairSync 在调用lock方法时会快速cas尝试修改state状态0->1,如果成功则设置锁持有者为自身,不去管是否有其他线程在等待。是非公平的方式。当cas不成功时,则调用acquire,并且NonfairSync 自身也和FairSync类一样,实现了acquire会调用的tryAcquire方法。tryAcquire方法使用的是父类Sync的非公平尝试获取锁方法nonfairTryAcquire

protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}


nonfairTryAcquire方法与FairSync类的tryAcquire不同点在于这个方法实现的不公平方式不会判断是否有前任在排队。直接进行尝试获取锁。会造成插队情况,是不公平获取锁的形式,但是比公平方式少了一些判断,性能更优。

nonfairTryAcquire的重入方式与公平锁FairSync的一样。

final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//如果状态为0,当前锁未锁住,尝试cas修改状态
if (compareAndSetState(0, acquires)) {
//设置当前线程为锁的独占线程
setExclusiveOwnerThread(current);
return true;
}
}
//如果占据锁的线程是当前线程,state+=acquires
else if (current == getExclusiveOwnerThread()) {

4000
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}


ReentrantLock unlock

ReentrantLock unlock调用的Sync的父类AQS的release方法释放锁。而Sync实现了release方法会调用的tryRelease。tryRelease会使锁状态state减少,调用一次则state减少1,代表重入次数减少一次。当减少到为0时,表示当前线程没有持有该锁,将锁的持有者置为空。

protected final boolean tryRelease(int releases) {
//state-=releases
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
//如果state为0,设置锁的所有者为空
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}


总结

ReentrantLock的重入为当持有锁的为当前线程时,重入,将锁状态state增加。

ReentrantLock的公平非公平模式,交由Sync的两个子类控制,区别在于公平模式只有没有前任在排队的情况下才可以获取锁。公平模式不进行这个判断。直接尝试获取锁,可插队。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: