ReentrantLock源码分析
ReentrantLock
是一个可重入的独占锁,内部使用AQS
实现。state
记录着持有锁的线程的进入同步代码块的次数。
1. AQS的基本实现Sync
Sync
是一个抽象类,他没有重写tryAcquire
方法,但是他多了一个nonfairTryAcquire
方法,该方法是一个非公平获取锁资源的方法。提供了一个抽象的lock
方法,这是一个统一的加锁方法。
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.
*/
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;
}
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
}
}
1.1 nonfairTryAcquire
因为在ReentrantLock
是支持公平锁和非公平锁的,这里的nonfairTryAcquire
方法就是非公平获取锁资源的实现。我们可以看到分两种情况,一是当没有线程持有锁时,直接cas修改state
,修改成功就获取到了锁,并将当前持有独占锁的线程设置为自己。二是判断当前自己是不是持有锁的线程(可能是重入),如果是就只需增加锁的重入次数。最后就是有其他线程占用了锁,返回false。
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;
}
1.2 tryRelease
进行锁资源的释放,该方法是线程安全的,因为独占模式只有一个线程只有锁,也就只有一个线程可以释放锁资源。释放锁刚好更获取锁资源反着来,减少锁的重入次数,设置当前持有锁的线程为null,当state
为0时,也就说明当前线程释放了锁资源,就返回true,从而唤醒后继节点。
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;
}
2. 公平和非公平模式
可以通过有参构造方法,指定使用公平锁还是非公平锁,默认无参构造方法是非公平锁。
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
2.1 非公平模式
实现了上面我们说的Sync
类型,补上对应两个需要实现的方法lock
和tryAcquire
,这里的tryAcquire
直接调用我们上面说的非公平获取锁资源的方法nonfairTryAcquire
。这里lock
方法本来可以直接调用acquire
方法,但是他在前面还是做了一次快速获取锁资源的尝试,直接casstate
,如果成功就设置持有锁的线程是当前线程,如果失败就再走一次正常的逻辑。
非公平性:代码我们可以看到,主要线程获取到了锁资源,就表示该线程持有了锁,不关系队列中释放有排在其前面的线程在等待。优点很明显,就是吞吐高;缺点也很明显,会导致线程饥渴,一直等待无法获取到锁资源(因为会被其他线程抢)。
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() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
2.2 公平模式
也是实现了lock
和tryAcquire
方法,这里的lock
方法就有了一点变化了,没有在lock
里面之前获取锁资源了,而是全部交给了acquire
来处理,这体现了公平性的一点,因为如果直接跟上面的非公平锁中lock
一样获取锁资源的话,就没办法办证公平性了。tryAcquire
中相比非公平锁,多了hasQueuedPredecessors
方法的判断,该方法用来判断当前节点是否有前驱节点,如果有,就获取锁失败,也就是说来获取锁资源的线程必须保证他没有前驱节点,其他代码基本和非公平锁一样。
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
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;
}
}
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
3. 实现了lock类
实现了统一的lock
接口,我们在使用时,一般是使用lock
接口中的方法进行加锁和解锁。这里的sync
就是构造方法中创建的NonfairSync
或FairSync
,另外newCondition
是创建一个条件队列对象。
public class ReentrantLock implements Lock, java.io.Serializable {
...
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);
}
public Condition newCondition() {
return sync.newCondition();
}
}
4. 总结
ReentrantLock
其实很简单,如果你看过AQS
的源码分析,那ReentrantLock
就是基于AQS
实现一种独占(排他)可重入的锁。他提供了公平和非公平模式,公平性是在获取锁资源时,检查当前节点是否有前驱节点。最后是实现了标准的Lock
接口,方便使用统一的lock
和unlock
进行加锁和解锁。