ReentrantLock原理及源码阅读
ReentrantLock原理及源码阅读
1、ReentrantLock介绍
ReentrantLock是可重入的独占锁,同时只能有一个线程可以获取到该锁,其他线程获取该锁的线程将会被阻塞而被放入该锁的AQS阻塞队列里面。
ReentrantLock最终还是使用AQS来实现的,并且根据参数来决定其内部是一个公平还是非公平锁。
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
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();
....
}
其中Sync类直接继承自AQS,它的子类NonfairSync
和FairSync
分别实现了获取锁的公平和非公平策略
2、获取锁的方法
void lock()方法
当一个线程调用该方法时,说明该线程希望获取该锁,如果锁当前没有被其他线程占用,并且当前线程之前没有获取过该锁,则当前线程会获取到该锁,然后设置当前锁的拥有者为当前线程,并设置AQS的状态值为1,然后直接返回。如果当前线程之前已经获取过该锁,则这次只是简单的把AQS的状态值加1后返回。如果该锁寂静被其他线程持有,则调用该方法的线程会被放入AQS队列后阻塞挂起。
public void lock() {
sync.lock();
}
ReentranLock的lock委托给了sync类,根据创建ReentranLock的构造函数选择sync的实现是非公平还是公平,这个锁是一个非公平锁或公平锁。以下是sync的子类NonfairSync的情况,也就是非公平锁。
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() {
// CAS 设置状态值state
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 获取失败则调用AQS的acquire方法
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
上述代码中,默认AQS状态值state为0,所以此一个调用lock的线程会通过CAS设置状态值为1,CAS成功则代表当前线程获取到了锁,然后通过setExclusiveOwnerThread
设置该锁的持有者为当前线程。
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
如果有其他线程调用lock方法获取该锁,CAS会失败,然后调用AQS的acquire
方法,传递的参数为1.
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
AQS并没有实现tryAcquire
方法,tryAcquire
方法需要子类自己定制化,所以上面的tryAcquire
实际上会调用ReentrantLock重写的tryAcquire方法。
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获取当前状态值。
int c = getState();
// 当前状态值为0则通过CAS获得锁
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;
}
// 为拿到锁返回false,然后该线程被放入AQS阻塞队列。
return false;
}
非公平锁的体现
:假设线程1调用lock方法时发现state状态值不为0,且当前线程不是锁的持有者则返回false,然后当前线程被放入AQS阻塞队列。这时线程2也调用了lock方法,发现当前状态值为0了(假设占有该锁的其他线程释放了该锁),所以通过CAS设置获取到了该锁。明明是线程1先请求获取的该锁,然而线程2获取到了锁,这就是非公平的体现。这里线程2获取锁前并没有查看当前AQS队列里面是否有比自己更早请求该锁的线程,而是使用了抢夺策略。
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 当前state为0.
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;
}
上述代码为公平锁的方法,公平锁的话只需要看FairSync重写的tryAcquire方法。
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());
}
上面代码中,是实现公平性的核心代码,如果当前AQS队列为空或者当前线程是AQS的第一个节点则返回false。其中如果ht则说明当前队列为空,直接返回false。如果h!=t且snull则说明有一个元素将要作为AQS的第一个节点入队列,那么返回true,如果h!=t并且s!=null和s.thread !=Thread.currentThread则说明队列里面的第一个元素不是当前线程,那么返回true。
3、释放锁的方法
void unlock()方法
public void unlock() {
sync.release(1);
}
尝试释放锁,如果当前线程持有该锁,则调用该方法会让该线程对该线程持有的AQS状态值减1,如果减去1后当前状态值为0,则当前线程会释放锁,否则仅仅只是减1而已。如果当前线程没有持有该锁而调用了该方法则会抛出异常。
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 如果不是锁的持有者,则抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果当前可重入次数为0, 则清空锁持有线程
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
// 设置可重入次数为原始值-1
setState(c);
return free;
}