ReentrantLock比Synchronized提供了更加灵活的加锁方式,可中古段一个正则等待锁的线程,实现指定时间的获取锁,实现公平与非公平锁等。
构造器及lock()
//默认构造非公平同步器
public ReentrantLock() {
sync = new NonfairSync();
}
//可构造公平或非公平同步器
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
//根据相应的同步器使用不同的获取锁的方式
public void lock() {
sync.lock();
}
//非公平式
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
//公平式
final void lock() {
acquire(1);
}
非公平锁和公平锁,都继承自ReentrantLock的静态内部类Sync,Sync继承自AQS。
每一个ReentrantLock都持有这一个Sync,根据构造方式的不同,Sync分别对应着公平锁和非公平锁。当ReentrantLock调用lock()方法进行加锁时,会根据持有的Sync的不同去调用不同的加锁方式。
如果是公平锁,线程将按照它们发出请求的顺序来获得锁,但在非公平锁上,则允许插队行为:当一个线程请求非公平的锁时,如果在发出请求的同时该锁的状态变为可用,那么这个线程将跳过队列中所有等待的线程直接获得这个锁。
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
//获取锁
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
Sync默认实现非公平锁,主要看看它的获取锁和释放锁的实现。
获取锁的步骤:
1、 获取当前线程以及锁的同步状态。
2、 如果同步状态为0,表示锁没有被占用,用CAS的方式设置当前线程为锁的拥有者(非公平式)。
3、 如果同步状态不为0,且当前线程是锁的拥有者,则同步状态增加(加1);如果同步状态不为0,且当前线程不是锁的拥有者,则获取锁失败。
释放锁的步骤:
1、同步状态减1。
2、如果当前线程不是锁的拥有者,则抛出异常。否则,执行3.
3、如果同步状态为0,表示当前线程不在占有锁,将锁的占有线程设为null;如果同步状态不为0,则更新同步状态。
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
//非公平式获取锁
@ReservedStackAccess
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取同步状态
int c = getState();
//同步状态为0 锁没有被占用
if (c == 0) {
//CAS设置同步状态
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;
}
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
//同步状态减值
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;
}
}
对于公平式锁,获取锁的步骤:
1、获取当前线程及锁的同步状态。
2、如果同步状态为0,表示锁没有被其他线程占有。先查询同步队列中是否有其他线程在排队(公平式获取),如果没有,则用CAS的方式设置同步状态,当设置成功后,设置当前线程为锁的占有者。
3、如果同步状态不为0,则
3.1 当前线程是锁的占有者,则获得锁,更新同步状态
3.2 当前线程不是锁的占有者,获取失败。
//公平的同步器
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
@ReservedStackAccess
protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获得同步状态
int c = getState();
//没有线程占有锁
if (c == 0) {
//如果没有前驱结点
/**
* 这时和非公平锁不同的是它会先去调用hasQueuedPredecessors方法查询同步队列
* 中是否有人在排队,如果没人在排队才会去修改同步状态的值,可以看到公平锁在
* 这里采取礼让的方式而不是自己马上去获取锁。除了这一步和非公平锁不一样之外,
* 其他的操作都是一样的。
*/
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;
}
}