是什么?
可重入:指当前线程获取锁之后可以再次获取这个锁,而不会被阻塞。
锁:实现了Lock接口,通过实现内部AQS子类,完成锁的实现。并且ReentrantLock是排他锁
排他锁与共享锁
对于JDK并发中,所谓的排他锁主要是实现了tryAcquire的锁(ReentrantLock、ReentrantReadAndWriteLock中的writeLock)。
共享锁是实现了AQS.tryAcquireShared的锁(ReentrantReadAndWriteLock中的读锁、Semaphore、CountDownLatch)
源码解析
阅读ReentrantLock需要对AQS有一定理解,请查看AQS解析
ReentrantLock实现了Lock接口,其中通过使用AbstractQueuedSynchronizer实现了公平锁与非公平锁。
ReentrantLock内部定义了一个AQS的抽象子类Sync。然后通过继承这个抽象类,分别实现了FairSync类、NonFairSync类 用来是先公平锁、非公平锁(默认)。并在这两个子类中实现了 AQS的tryAcquire模板方法。公平与非公平锁的区别主要体现在tryAcquire的实现上:
先看ReentrantLock.Sync:
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing is to
* allow fast path for nonfair version.
*/
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in subclasses,
* but both need nonfair try for trylock method.
*/
// 非公平式获取锁,非公平锁barge失败后会执行。lock(非公平模式)、tryLock的实现逻辑
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {// 当前锁是否空闲
if (compareAndSetState(0, acquires)) {// CAS获取锁
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);// 更新state状态为当前重入次数
return true;
}
return false;// CAS失败 、也不属于当前线程重入的情况。获取锁失败,返回
}
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;
}
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
接着看ReentrantLock.Sync的两个子类.
ReentrantLock.NonfairSync
/**
* 非公平锁(ReentrantLock默认模式):
* 1.直接使用CAS去尝试获取锁(也就是同步状态state),
* 成功后当前线程置为拥有排他锁的线程。返回
*
* 2.如果失敗调用AQS-acquire(),与公平锁的区别主要体现在tryAcquire上。
* 判断当前同步状态是否空闲:
* 空闲:尝试CAS获取锁,成功则返回true否則返回false,进入同步队列等待逻辑。
* 占用:考虑是否是同一个线程重入情况,否則返回false。
*
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
// 非公平锁:直接CAS闯入式获取锁.
if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);// barge失败,常规式获取锁,特点就是在tryAcquire中增加了重入累计的逻辑。详细见AQS代码详解。
}
// 实现了AQS的模板方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
ReentrantLock.FairSync.
/**
* 公平模式:实现的AQS模板方法tryAcquire。
* 如果当前锁空闲:
* 先判断当前线程是否是等待最久的线程
* (也就是判断当前线程结点是否是头结点的后继结点,从而保证FIFO公平性):
* 如果是那么就CAS获取锁,失败后返回false,进入AQS-FIFO队列等待
* 如果当前锁被占用:考虑是否是同一个线程重入的情况,否则返回false,进入AQS-FIFO队列等待
* 公平锁与非公平锁主要区别在于tryAcquire的实现上:
* 前者首先会通过CAS强行获取锁(两次CAS尝试),获取失败进入FIFO队列等待;
* 后者获取锁先判断是否“公平”,如果满足“公平”则进行CAS操作。不公平则直接进入FIFO队列等待
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {// 当前锁空闲
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {// !当前线程之前是否还有等待时间更长的结点(也就是說头结点的后继者是否是当前线程)&&CAS获取锁
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实现。
另一篇文章解析了AQS,此处不赘述。
可重入锁:
持有锁的当前线程不变的条件下,通过CAS完成state++,完成锁的重入。每次统一线程重入累加一,这个线程释放一次,减一。直到state==0,锁被彻底释放。
公平与非公平锁
从源码可以看出,所谓"公平"是等待时间较长(头结点的后继者,!hasQueuedPredecessors()判断)的会优先获取锁,
符合FIFO。如果不满足公平性,直接进入等待队列。这会产生较多的线程切换。
而"非公平“则是不考虑线程的等待时间,直接尝试CAS获取锁,自由竞争。
非公平锁可能会导致某个线程长时间处于饥饿中,但是极少线程切换,提升锁的获取成功率,增大系统吞吐量。默认时,ReentrantLock使用非公平锁。
无论公平锁、非公平锁,如果CAS获取失败都会进入FIFO队列,等待再 次获取锁。
ReentrantLock构造器:
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
关于Condition,请查看java并发编程——Condition