ReentrantLock的实现依赖于Java同步器框架AbstractQueuedSynchronizer(本文简称之为AQS)
reentrantLock是可重入锁
实现重进入
重进入是指任意线程在获取到锁之后能够再次获取该锁而不会被锁所阻塞,该特性的实
现需要解决以下两个问题。
1)线程再次获取锁。锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再
次成功获取。
2)锁的最终释放。线程重复n次获取了锁,随后在第n次释放该锁后,其他线程能够获取到
该锁。锁的最终释放要求锁对于获取进行计数自增,计数表示当前锁被重复获取的次数,而锁
被释放时,计数自减,当计数等于0时表示锁已经成功释放。
我们以ReentrantLock来开始分析,
首先看下简单的使用
再来看下ReentrantLock的源代码,ReentrantLock是实现Lock接口和Serializable接口,有一个内Abstract的sync类继续了AbstractQueuedSynchronizer,可以看到Lock的类图
可以看到Sync有如下 方法
我们可以看到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.
*/
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
}
}
ReentrantLock 中的内部类的Sync有两个子类FairSync(公平锁)、NonfairSync(非公平锁)
我们可以看到代码获取锁时调用的是lock.lock,在ReentrantLock源码中可以看到,lock实际调用的是syn.lock
接下来看下sync是什么 ,可以看到sync是Sync的一个变量
现在我们看下非公平锁中的lock是什么呢?
可以看到调用了compareAndSetState方法通过CAS的方式获取锁如果获取到锁就执行,该方法是AQS中的一个方法
compareAndSwapInt方法是一个native方法,如有兴趣可以查看jvm相关的内容。
该方法只是改变当前线程的拥有者
如果没有获取到锁,则调用acquire(1),该方法调用AQS方法
可以看到AQS中的if语句中调用了tryAcquire方法,如果成功则表示获得了锁(独占锁)
该方法是AQS的一个protected方法,该处会调用NonfairSync中的tryAcquire方法
接下来我们看下NonfairSync中的,
该处是调用父类的Syn的方法,看一下Syn该方法是什么内容
首先要通过getState()获取状态,返回当前线程的状态state如果为0表示没有获得锁,其他数字表示该线程获取的次数 ,需要调用tryRelease() 来改变state状态,如果state为n那到要释放n次直到为0 才会释放锁,否则不会释放锁,上图的后面部分代码就是实现该功能。
现在我们回到AQS的acquire方法
在tryAcquire返回为true的情况下,我们来看下
acquireQueued方法,可以看到该方法是需要一个节点,由此可以猜测addWaiter方法是为当前线程构建一个节点,addWaiter方法是通过独占模式将node放到AQS等待队列(可以看AQS的原理)当中,具体体现在该行代码enq(node);acquireQueued方法是通过自旬的方式一直到获取到锁为止。
构建节点
将节点添加到等待队列末尾
lock的时序图
到这里lock就分析完了,接下来看unlock,unlock调用的是Sync静态内部类的release方法
sync.release调用的是AQS中的release方法
接下来调用
Sync静态类中的tryRelease方法,可以看到该方法是将statue的值减1,如果值为0表示可以释放锁。
释放锁也分析完毕
公平锁在释放锁的时候与非公平锁的方法是一样的,只是在获得锁的时候略有区别,下面我们来看下具体的不同,我们可以看到公平锁在获取锁的时候比非公平锁多调用了一个方法
hasQueuedPredecessors()方法,该方法判断当前节点为是否为头节点,保证严格按照FIFO(先进先出的方式)来获取锁
hasQueuedPredecessors