前言
ReentrantLock类是实现了Lock接口的可重入锁,所谓可重入锁指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其它线程是不可以的。
首先看下ReentrantLock类的结构
这个类中包含了一个Sync抽象类和一个NonfairSync非公平锁的实现了还有一个FairSync公平锁的实现类, Sync抽象类继承自AbstractQueuedSynchronizer类,NonfairSync和FairSync都继承了Sync
ReentrantLock类有两个构造函数
分别是
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
如果构造函数中传入true则使用的是公平锁,为false则采用的是非公平锁,默认构造函数则采用的是非公平锁
那么什么叫做锁什么又叫做非公平锁呢?
公平锁即尽量以请求锁的顺序来获取锁。比如,同时有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该锁,这种就是公平锁。
而非公平锁则无法保证锁的获取是按照请求锁的顺序进行的,这样就可能导致某个或者一些线程永远获取不到锁。
正文
一、当调用lock()方法的时候底层都做些了什么?
非公平锁lock方法的实现
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
公平锁lock方法的实现
final void lock() {
acquire(1);
}
非公平锁在调用lock方法的时候会先以CAS的形式将同步状态stat设置为1,成功的话就会将当前线程绑定到该锁的对象上,表示当前线程独占锁,如果不成功则调用acquire方法
公平锁在调用lock方法的时候则直接调用acquire方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
在acquire方法中会尝试获取独占锁,如果不成功的话则加入到AQS的同步队列中去
非公平锁的tryAcquire方法的实现
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取同步状态
int c = getState();
if (c == 0) {
// 如果同步状态为0则以CAS的形式将同步状态设置为1
if (compareAndSetState(0, acquires)) {
// 设置成功的话则将当前线程绑定到当前的锁对象上,同时返回true
setExclusiveOwnerThread(current);
return true;
}
}
// 不成功的话则判断则判断当前的锁释放是当前线程独占的,如果是的话则将同步状态+1(以线程和状态变量实现锁的可重入)
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// 以上都不成功的话返回false
return false;
}
公平锁的tryAcquire方法的实现
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
s(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;
}
分析上面两个公平锁与非公平锁的tryAcquire方法的实现不难发现公平锁比非公平锁多了一个判断 !hasQueuedPredecessors()判断同步器队列中当前线程是否处于head节点的next如果是的话才再次进行获取锁或者说只要同步队列中没有等待的锁。
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
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());
}
综上不管是公平锁还是非公平锁在获取锁失败的都会将当前线程封装成Node对象,挂在AQS同步队列的尾节点上。
二、当调用unLock()方法的时候底层都做些了什么?
释放锁的方法
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
该方法的调用只可能是单线程调用
protected final boolean tryRelease(int releases) {
// 同步状态-1
int c = getState() - releases;
// 判断当前锁释放是当前线程锁独占的,如果判断不成立则抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 当c==0则表示当前线程已经释放了它多次获取到的锁(因为是可重入的,当前线程可能多次获取该锁,例如递归方法)
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}