概念
ReentrantLock是一种可重入锁,即一个线程可以对同一个资源反复的加锁。同时,该锁还可以设置公平与非公平。公平锁是按线程的到达顺序进行获取锁操作,而非公平锁是抢占式。所以公平锁的效率是没有非公平锁高的,但是公平锁可以减少“线程饥饿”发生概率。
与syncchronized的比较
synchronized:是一种同步锁,串行锁,他是基于jvm实现的,通过montionentr指令加锁,monitionexit指令解锁。是一种隐式的可重入锁。而且无法中断一个在等待的线程
reentrantlock:是基于Java代码实现的,可重入,可以中断正在等待的线程。
使用场景
用在界面交互时点击执行较长时间请求操作时,防止多次点击导致后台重复执行(忽略重复触发)。
比如说在安卓中,转到native页面时由于网络慢可能要等待一下,这是候用户可能会反复点击,如果不加重入锁等网络通畅时将产生无数个native界面,有可能使APP内存爆掉。
在concurrentHashmap就是通过用reentrantlock来锁segment,实现锁分段的。
优缺点
优点:可重入,可以使用conditon进行灵活的控制
缺点:必须在finilly中释放
源码分析
reentrantlock实现了lock接口
public class ReentrantLock implements Lock, java.io.Serializable
里面通过sync这个抽象静态类实现加锁解锁,默认锁是非公平的
abstract static class Sync extends AbstractQueuedSynchronizer{
final boolean nonfairTryAcquire(int acquires){...}
protected final boolean tryRelease(int releases){...};
.......
}
Sync这个类只是一个基类,通过nofairsync与fairsync继承sync来实现非公平锁与公平锁
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
} //非公平锁加锁解锁直接使用的就是sync的方法
fairsync公平锁
static final class FairSync extends Sync {
@ReservedStackAccess
protected final boolean tryAcquire(int acquires) {
......//重行实现了tryAcquire
}
}
好了,下面我们来看一下非公平锁的加锁方法「不公平的加锁方法,如果state为0,则使用原子操作更新state,然后设置当前锁的拥有者为当前线程」
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
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;
}
在看一下公平的加锁方法「比不公平加锁多了一步判断【hasQueuedPredecessors(),这个函数是判断阻塞队列里面当前线程前面是否有等待的线程,有的话就让前面的先获取锁】然后与上面非公平的一样
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;
}
解锁操作公平与非公平都是一样的,当state为0时,free = true 将当前锁占有者线程设为null「setExclusiveOwnerThread(null)」;只有当state为0才能释放,如果state != 0 ,free=flase,返回free
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
reentrantlock默认是非公平的
public ReentrantLock() {
sync = new NonfairSync();
}
reentrantlock构造函数传入fair以设置公平
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}