介绍
行为与语义和synchronized一模一样。但是,ReetrantLock拓展了一些功能。
使用
使用上来说,LinkedBlockingQueue已经给了实践:ReetrantLock+Condition。一篇不错的使用文章ReetrantLock应用
阅读
ReetrantLock的阅读,核心就是AQS:AbstractQueuedSynchronizer。而AbstractQueuedSynchronizer的核心就是CLH锁队列。CLH是3个人,他们发明了CLH锁。
api上的认知点击这里。
如何实现点击这里。
额外补充下CLH锁的介绍点击这里
api,实现两者的阅读已经挺完整了,但需要你自己去抓核心。这里我简单理一条线,并凸显下AbstractQueuedSynchronizer中CLH锁队列的使用。这条线的顺序是lock(ReentrantLock)->acquire(AbstractQueuedSynchronizer)->tryAcquire(子类同步器实现)
lock
final void lock() {
// 这个方法调用的理解很重要!
// 这里涉及到的是对AbstractQueuedSynchronizer的state字段。
// state代表的是AbstractQueuedSynchronizer同步器的同步状态,简单理解就是这个锁目前有没有被线程占有(0:没有,>1:被占有,当前线程对这个锁的重入次数)
if (compareAndSetState(0, 1))
// 这个涉及到AbstractQueuedSynchronizer的
// exclusiveOwnerThread字段,代表当前持有独占锁的线程
// 此时,lock执行完毕,不会产生阻塞。
setExclusiveOwnerThread(Thread.currentThread());
else
// 所以,这个else里面肯定会产生阻塞了。
acquire(1);
}
protected final boolean compareAndSetState(int expect, int update) {
return U.compareAndSwapInt(this, STATE, expect, update);
}
acquire
public final void acquire(int arg) {
// 这里tryAcquire放到下面一个节点讲述。
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
*** 当tryAcquire获取锁(体现到代码就是设置同步器的state为1)失败的时候,
会先进行addWaiter再acquireQueued ***。
private Node addWaiter(Node mode) {
// 产生这个Node的理解挺重要。这里不发散,直接说下作用
// 产生Node节点,nextWaiter字段指向mode(null),调用lock方法处的线程存入thread字段。
// 所以,这里大家可以去看看Node到底有哪些字段。
Node node = new Node(mode);
// 这里是操作CLH锁队列的核心了!!!
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
// 个人认为这里可以直接node.prev = oldTail
U.putObject(node, Node.PREV, oldTail);
// 这句话的作用是:将tail更新指向我们上面新增的node。
// 通过unsafe保证并发修改的准确性。
if (compareAndSetTail(oldTail, node)) {
// oldTail是局部变量,不会发生并发修改的问题。
// next字段是volatile类型,保证其可见性
// happen-before,保证对next字段的读取发生在修改之后
oldTail.next = node;
return node;
}
} else {
// tail为空时,代表未进行初始化。
// 初始化的动作其实也很简单,通过unsafe的compareAndSwapObject
// 保证并发操作共享资源的准确性。初始化一个Node赋值给head,tail
initializeSyncQueue();
}
}
}
final boolean acquireQueued(final Node node, int arg) {
try {
boolean interrupted = false;
// 看到很多这种循环了,其实就是CAS操作失败自旋。
// 当你一定需要CAS操作成功的时候,通常会以这种方式产生自旋,一直等待到CAS操作成功。
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
return interrupted;
}
// 自旋锁的变种(不是不断循环,而是阻塞)实现
// shouldParkAfterFailedAcquire根据preNode的waitstatus
// 决定是否阻塞,不阻塞的话就进入外层的for循环即自旋了。
// parkAndCheckInterrupt通过Unsafe.park进入阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} catch (Throwable t) {
cancelAcquire(node);
throw t;
}
}
以上lock操作如何进入自旋,阻塞的过程已经完整。注释中关于Unsafe的描述是对其并发性的一个理解。如果想看他的lock上锁流程,可以忽略对unsafe的理解。
tryAcquire
tryAcquire的理解可以让你更好的知道:ReentrantLock中公平锁,非公平锁的区别。这里先给出区别,代码阅读就先不写了,直接去看各自实现。 公平,非公平的区别其实只存在于子同步器FairSync和NonfairSync的tryAcquire实现。具体表现就是:
- 当state为0是,非公平锁会直接尝试去获得锁,成功返回true,这次lock就执行完毕,不产生任何自旋和阻塞。而公平锁会先看同步器中CLH队列是否已存在节点。