通过下述代码,来深挖ReentranLock的底层实现。下述代码使用了ReentrantLock锁的lock、unlock、condition等常用方法。让我们一一解析底层实现。
public class TestReentrantLock {
private static final int MAX_CACHE_SIZE = 16;
private Map<String, String> cache = new HashMap<>();
private ReentrantLock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public void put(String key, String value) {
lock.lock();
try {
while (cache.size() == MAX_CACHE_SIZE) {
notFull.await();
}
cache.put(key, value);
notFull.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void remove(String key) {
lock.lock();
try {
while (cache.isEmpty()) {
notEmpty.await();
}
cache.remove(key);
notFull.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
lock.lock()就是申请锁,底层调用代码如下:
public void lock() {
sync.lock();
}
其中,sync是ReentrantLock对象中的内部类abstract static class Sync extends AbstractQueuedSynchronizer。能够看到Sync是一个抽象类,它有两个子类FairSync(公平锁)和NonFailSync(非公平锁)。两个都实现了lock()方法。ReentrantLock默认是非公平锁,而非公平锁优于公平锁本质是:公平锁比非公平锁多了线程的阻塞和唤醒操作,而java中线程需要内核线程支持,线程的调度、阻塞、唤醒都需要从用户态切换到内核才行,对性能影响是比较大的。
final void lock() {
acquire(1);
}
acquire是来自AQS的获取锁的框架:tryAcquire()是提供给子类的抽象方法,用于具体获取锁的逻辑;addWaiter()是向内部维护的双端队列中添加封装了thread的Node节点,acquireQueued()是判断Node节点是否为head后继节点,如果是则表示该节点是第一个节点,则尝试获取锁,如果不是head的后继节点,则尝试阻塞node节点,阻塞失败则循环上述逻辑,直到获取锁或阻塞。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
下述为具体的源码细节。
tryAcquire()是AQS提供给子类的获取锁的抽象方法。在AQS中,int state是提供给锁意义的属性。在互斥锁ReentrantLock中,state==0说明,锁没有被任何线程占有,而state==1时,说明线程被某线程持有,state>1时,说明线程的重入次数。因此:获取锁的基本思路就是基于state属性值得。当state==0时,尝试获取锁;当state!=0时,判断线程是否被自身持有,如果不是则获取锁失败,否则就是更新state的值,增加它的重入次数。
protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取int state的值,state在不同锁中代表的含义不一样。共享锁中代表共享变量的数量,在独占锁中表示锁是否被独占以及重入次数;
//ReentranLock重入锁中,等于0时,表示锁没有被独占;大于等于1时,表示被独占且重入的次数。
int c = getState();
if (c == 0) {
//hasQueuedPredecessors()表示AQS队列中是否存在其他阻塞线程,存在时,获取锁失败。此处也是公平锁和非公平锁的主要差别。
// 如果队列中不存在阻塞线程,通过cas尝试设置state值为acuires,同时,通过setExclusiveOwnerThread(thread)将该线程设置为锁的独占线程
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 通过getExclusiveOwn