ReentrantLock
一、源码
类图
- ReentrantLock实现了Lock接口,加锁和解锁都需要显式写出,注意一定要在适当时候unlock。
公平锁和非公平锁
- 公平锁:线程获取锁的顺序和调用lock的顺序一样,FIFO;
- 非公平锁:线程获取锁的顺序和调用lock的顺序无关,全凭运气。
ps:
ReentrantLock默认使用非公平锁是基于性能考虑,公平锁为了保证线程规规矩矩地排队,需要增加阻塞和唤醒的时间开销。如果直接插队获取非公平锁,跳过了对队列的处理,速度会更快。
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
ReentrantLock的内部类Sync继承了AQS,分为公平锁FairSync和非公平锁NonfairSync。
ReentrantLock的公平锁和非公平锁都是基于AbstractQueuedSynchronizer(AQS)实现的。ReentrantLock使用的是AQS的排他锁模式,由于AQS除了排他锁模式还有共享锁模式,本文仅对ReentrantLock涉及到的排他锁模式部分的内容进行介绍,关于共享锁模式的部分会在 CountDownLatch 源码浅析一文中介绍。
公平锁和非公平锁获取锁的对比
公平锁加锁调用顺序
- ReentrantLock:lock()
- FairSync:lock()
- AbstractQueuedSynchronizer:acquire(int acquires)
- FairSync:tryAcquire(int acquires)
其中第四步才开始加锁,源码如下:
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;
}
非公平锁加锁调用顺序
- ReentrantLock:lock()
- NonfairSync:lock()
- Sync:nonfairTryAcquire(int acquires)
- AbstractQueuedSynchronizer:compareAndSetState(int expect, int update)
第三步源码
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;
}
对比
- 可以看出公平锁的加锁多了一个判断
!hasQueuedPredecessors()
, 即加入了同步队列中当前节点是否有前驱节点的判断,如果该方法返回true,则表示有线程比当前线程更早地请求获取锁,因此需要等待前驱线程获取并释放锁之后才能继续获取锁。 - 加锁最终都是调用的AQS的compareAndSetState方法
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
// 设置锁的持有者为当前线程
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
公平锁和非公平锁的解锁
解锁步骤是一样的
调用顺序
- ReentrantLock:unlock()
- AbstractQueuedSynchronizer:release(int acquires)
- Sync:tryRelease(int acquires)
其中acquires=1,看下tryRelease方法:
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;
}
分析
公平锁在释放锁的最后写volatile变量state;在获取锁时首先读这个volatile变量。根据volatile的happens-before规则,释放锁的线程在写volatile变量之前可见的共享变量,在获取锁的线程读取同一个volatile变量后将立即变的对获取锁的线程可见
二、原理分析
CAS[原子性]+volatile[可见性]