锁专题(五)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 给出的解释如下:
即使将此锁设置为使用公平的排序策略 , 对 tryLock() 的调用也会立即获取该锁(如果有) , 无论当前是否有其他线程在等待该锁 。 即使破坏公平性 , 这种插入行为在某些情况下也可能有用 。
- 报名啦!宿迁开展第五届“十大科技之星”评选
- 苹果|iPhone13迎来变化!或回归指纹解锁,这几点备受用户喜爱
- 星期一|亚马逊:黑五与网络星期一期间 第三方卖家销售额达到48亿美元
- 绝不能|互联网也有“五连鞭”毒瘤,绝不能让这些“割韭菜者”一跑了之
- 早报:高通骁龙888正式发布 嫦娥五号传回首张图片
- 网购|黑色星期五及网购星期一大数据出炉 全球第三方卖家销售额超48亿美元
- 摄像头|华为Mate40或采用五摄加一传感器,摄像头越多真越好
- 手机|预算只有五千,买4G的iPhone11还是买国产5G安卓机?
- 至上|男人的工具箱:五金工具跟着我来买,实用至上,绝不吃灰
- 公园|长沙五一广场、烈士公园…湖南5G速度最快的地方是?