锁专题(五)ReentrantLock 深入源码详解( 二 )

这里使用独占模式获取锁 , 忽略打断 。
而且会至少调用一次 tryAcquire() 方法 , 成功就返回 。 没成功 , 就是放在重试队列里 , 一直重试 , 直到成功为止 。
所以本质上 , acquire 方法调用的还是 tryAcquire() 方法 , 额外多了重试功能 。
小思考题为什么说这是一种非公平锁?
公平锁实现源码公平锁也是继承自 Sync 类 。
/** * Sync object for fair locks * @author 老马啸西风 */static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;/*** 加锁方法 , 比较简单就是直接调用 acquire()*/final void lock() {acquire(1);}/*** Fair version of tryAcquire.Don't grant access unless* recursive call or no waiters or is first.*/protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {if (!hasQueuedPredecessors()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 方法我们来重点看一下 tryAcquire() 方法 。
这里首先如果 state=0(没有线程持有锁) , 则尝试开始加锁 。 但是有一个前提 , 那就是这个方法:!hasQueuedPredecessors()
这个方法意思也比较明确 , 就是在当前线程之前没有其他线程等待 , 就像排队一样 , 讲究一个先来后到 。

  • hasQueuedPredecessors()
这个方法我们简单看一下 , 不是本节的重点内容:
/** * @author 老马啸西风 */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 orderNode h = head;Node s;return h != t }当前线程前面有其他线程等待的条件如下:
(1)等待的队列不是空 。
(2)第一个元素不是当前线程 。
什么叫可重入?实际上 current == getExclusiveOwnerThread() 分支的处理方式 , 这里就是可重入的概念 。
如果当前线程和持有锁的线程是同一个 , 直接操作即可 , 省去了上面的判断 。
构造器构造器相对理解起来就轻松很多 。
默认是非公平锁 , 也可以指定是否公平来创建 。
/** * Creates an instance of {@code ReentrantLock}. * This is equivalent to using {@code ReentrantLock(false)}. * @author 老马啸西风 */public ReentrantLock() {sync = new NonfairSync();}/** * Creates an instance of {@code ReentrantLock} with the * given fairness policy. * * @param fair {@code true} if this lock should use a fair ordering policy */public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}为什么默认是非公平锁?个人理解是处于性能的考虑 , 非公平和公平锁对比 , 少了一次判断是否为第一个等待获取锁的线程比较 。
当然公平锁的优点就是避免饥饿 。
其他方法剩下的方法 , 大部分都是对 Sync 实现的简单调用 , 让我们来一起看一遍 。
lock()加锁方法 , 不允许被打断 。 直接调用 sync 。
public void lock() {sync.lock();}lockInterruptibly()允许被打断的加锁方式 。
public void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}这个调用的也是 AQS 的方法:
/** * @author 老马啸西风 */public final void acquireInterruptibly(int arg)throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();if (!tryAcquire(arg))doAcquireInterruptibly(arg);}如果当前被打断 , 直接抛出打断异常 。
如果尝试获取锁失败 , 则调用 doAcquireInterruptibly() , 方法实现如下:
/** * Acquires in exclusive interruptible mode. * @param arg the acquire argument * @author 老马啸西风 */private void doAcquireInterruptibly(int arg)throws InterruptedException {final Node node = addWaiter(Node.EXCLUSIVE);boolean failed = true;try {for (;;) {final Node p = node.predecessor();if (p == headp.next = null; // help GCfailed = false;return;}if (shouldParkAfterFailedAcquire(p, node)}} finally {if (failed)cancelAcquire(node);}}这里就是在循环等待获取锁 , 用独占的方式 , 只不过允许被打断 。
tryLock()尝试获取锁 , 实现如下:
public boolean tryLock() {return sync.nonfairTryAcquire(1);}这里非常有趣 , 调用的是非公平尝试获取锁的方法 。
  • 为什么调用的是非公平锁?
我们都声明使用公平锁模式了 , 为什么 jdk 在这里用的确是非公平模式获取的?
jdk 给出的解释如下:
即使将此锁设置为使用公平的排序策略 , 对 tryLock() 的调用也会立即获取该锁(如果有) , 无论当前是否有其他线程在等待该锁 。 即使破坏公平性 , 这种插入行为在某些情况下也可能有用 。