博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ReentrantLock 源码解析
阅读量:5103 次
发布时间:2019-06-13

本文共 5458 字,大约阅读时间需要 18 分钟。

1、个人总结和看法:

(1)、AQS和ReentrantLock的关系?

ReentrantLock是基于AQS的实现的,昨天我们说了AQS的tryAcquire()是默认抛出异常的需要子类去重写逻辑,ReentrantLock就重写了tryAcquire()。这样就解释了之前的疑问,因为这本来就是留给子类自己去完成的逻辑。

(2)、ReentrantLock的锁模式?

默认是非公平获取锁,不过可以在构造是设置公平锁模式获取。

(3)、为什么ReentrantLock的锁模式默认为非公平锁?

我的理解是当在竞争锁时,如果为非公平锁模式,那么当有新开启的线程在和队列中的线程竞争时,新开启的线程会更容易获取锁,因为队列唤醒需要更多操作,这样的避免了入队和队列唤醒的操作开销,节约了很多的性能,并发原理分析始终要从安全性和性能考虑。

 

2、内部结构解析:

(1)、ReentrantLock有一个内部抽象类 Sync,有一个lock抽象方法,它继承了AQS也就解释了ReentrantLock和AQS的关系。还有两个抽象类FairSync和NonfairSync 他们都继承了Sync,重写了lock方法,分别为公平和非公平获取锁的实际处理逻辑。

(2)、构造方法

1 public ReentrantLock() {2         //默认为非公平获取锁3         sync = new NonfairSync();4     }
1 public ReentrantLock(boolean fair) {2 //也可以传入参数为true就表示公平锁3         sync = fair ? new FairSync() : new NonfairSync();4     }

 

3、重点方法源码分析:

(1)、非公平锁获取

1 static final class NonfairSync extends Sync { 2         private static final long serialVersionUID = 7316153563782823691L; 3  4         final void lock() { 5 //首先设置状态位  6             if (compareAndSetState(0, 1)) 7 //如果设置状态位成功 就设置独占线程 8                 setExclusiveOwnerThread(Thread.currentThread()); 9             else10 //失败后竞争锁11                 acquire(1);12         }13 14         protected final boolean tryAcquire(int acquires) {15 //非公平锁获取锁的实际逻辑16             return nonfairTryAcquire(acquires);17         }18     }

总结:所以你能看到其实ReentrantLock当设置独占线程失败后是直接调用的AQS中的方法,进行入队和其他操作。这里应该将ReentrantLock和AQS的关系体现的比较明显了,重点是看ReentrantLock重写的tryAcquire方法。

 

acquire() 源码分析:

1 public final void acquire(int arg) {2        //这里我们昨天分析过了  就是AQS的方法 3         if (!tryAcquire(arg) &&4             acquireQueued(addWaiter(Node.EXCLUSIVE), arg))5             selfInterrupt();6     }

这里其实就是AQS的方法。

 

nofairTryAcquire()方法源码分析:

1 final boolean nonfairTryAcquire(int acquires) { 2           //获取当前线程 3             final Thread current = Thread.currentThread(); 4 //获取状态位 5             int c = getState(); 6             if (c == 0) { 7 //如果没有被设置 就设置 并设置独占线程为自己 8                 if (compareAndSetState(0, acquires)) { 9                     setExclusiveOwnerThread(current);10                     return true;11                 }12             }13 //如果设置了  就检查独占线程是不是自己  是自己的话那就再次获取 这就是可重入锁14             else if (current == getExclusiveOwnerThread()) {15                 int nextc = c + acquires;16                 if (nextc < 0) // overflow17                     throw new Error("Maximum lock count exceeded");18                 setState(nextc);19                 return true;20             }21 //否则返回false22             return false;23         }

这里我们的非公平方式获取锁的逻辑就明白了  所以调用流程其实是(假设存在竞争,并且失败,因为这个流程比较复杂,其他相对简单)lock()、acquire()、tryAcquire()、nofairTryAcquire()、addWaiter()(其他是AQS参看AQS个人分析)

所以ReentrantLock其实是在AQS的基础上完成的,自己也就是设置独占线程这种简单操作,当遇到竞争时操作都是AQS的逻辑完成。

 

(2)、非公平锁的释放

释放相对于简单一些,昨天我们并没有分析AQS的释放 所以我们现在分析一下,直接上源码

我还是喜欢根据逻辑来分析 这样也方便大家分析

unLock()源码分析:

1 public void unlock() {2 //实际上是Sync的释放  再向下转型到非公平锁3         sync.release(1);4     }

 

 

release()源码分析:

1 //这是AQS中的方法了 2 public final boolean release(int arg) { 3  4 //尝试释放  我们应该能猜到tryRelease是子类实现的 5         if (tryRelease(arg)) { 6 //获取头节点 7             Node h = head; 8 //如果不为空且不为等待状态 9             if (h != null && h.waitStatus != 0)10 //释放头节点11                 unparkSuccessor(h);12 释放成功返回true13             return true;14         }15         return false;16     }

 

tryRelease()分析:

1  protected final boolean tryRelease(int releases) { 2 //获取状态位   3             int c = getState() - releases; 4 //如果当前线程不是持有锁的线程就抛出异常 5             if (Thread.currentThread() != getExclusiveOwnerThread()) 6                 throw new IllegalMonitorStateException(); 7             boolean free = false; 8 //如果状态位为0  则表示已经成功的释放了锁  9             if (c == 0) {10                 free = true;11 //设置独占线程为null12                 setExclusiveOwnerThread(null);13             }14 //否则返回当前状态位 15             setState(c);16             return free;17         }

 

 其实ReentrantLock是通过一个volatile的state来表示当前所的状态,其实就是内部规定的机制而已,这就是抽象意义的锁。就是所有权。

 

 (3)、公平锁获取:

因为和非公平锁的性质类似 我们主要分析不同的地方,直接上源码吧。

1 static final class FairSync extends Sync { 2         private static final long serialVersionUID = -3000897897090466540L; 3  4         final void lock() { 5 //重写了lock方法 6             acquire(1); 7         } 8  9      10         protected final boolean tryAcquire(int acquires) {11 //获取当前线程12             final Thread current = Thread.currentThread();13             int c = getState();14             if (c == 0) {15 //这里和非公平不同的是,它就算知道锁没有被占有,还是会去检查自己前面还有没有等待的线程  公平锁嘛  必须要前面没有线程等待了 才会去获取锁16                 if (!hasQueuedPredecessors() &&17                     compareAndSetState(0, acquires)) {18                     setExclusiveOwnerThread(current);19                     return true;20                 }21             }22             else if (current == getExclusiveOwnerThread()) {23                 int nextc = c + acquires;24                 if (nextc < 0)25                     throw new Error("Maximum lock count exceeded");26                 setState(nextc);27                 return true;28             }29             return false;30         }31     }

 

请注意我在代码注释里面说的和非公平锁的不同点 这是重点

我们看看hasQueueedPredecessors()的源代码吧:

 

1 public final boolean hasQueuedPredecessors() {2        //从代码里面很容易看到逻辑是3 //头节点初始化了 并且当前节点是第一节点4         Node t = tail; // Read fields in reverse initialization order5         Node h = head;6         Node s;7         return h != t &&8             ((s = h.next) == null || s.thread != Thread.currentThread());9     }

 

 

 

释放节点就不分析了 差不多

转载于:https://www.cnblogs.com/yangquan95/p/8472407.html

你可能感兴趣的文章
服务器解析请求的基本原理
查看>>
pycharm 如何设置方法调用字体颜色
查看>>
VUE源码解析心得
查看>>
[HDU3683 Gomoku]
查看>>
【工具相关】iOS-Reveal的使用
查看>>
整体二分——[Poi2011]Meteors
查看>>
数据库3
查看>>
delphi之事件
查看>>
windows server 2008 r2 安装
查看>>
Enigma –> Sadness
查看>>
存储分类
查看>>
下一代操作系统与软件
查看>>
【iOS越狱开发】如何将应用打包成.ipa文件
查看>>
[NOIP2013提高组] CODEVS 3287 火车运输(MST+LCA)
查看>>
Hat’s Words (分成两个字符串考虑)
查看>>
Yii2 Lesson - 03 Forms in Yii
查看>>
Python IO模型
查看>>
Ugly Windows
查看>>
DataGridView的行的字体颜色变化
查看>>
java.nio异步线程安全的IO
查看>>