多线程-aqs与reentrantLock
AQS和ReentrantLock
ReentrantLock的lock特点
-
异常的锁是包含死锁和活锁,死锁无法解决,活锁可以通过错开执行时间解决
-
lock的特点:可打断,可重入,可设置超时时间,可设置锁类型(公平非公平),支持多变量,支持锁类型(读,写)
-
以上都是基础概念,例子就不写了,至于多变量condition,这个我也没用过,大概意思,用对应的condition去唤醒,下面是例子
AQS框架的样子
-
AbstractQueuedSychronizer 同步队列,他有一个属性state表示锁的状态,数字表示获得锁的次数,利用cas改变其数值;其中有俩吧锁,独占锁,共享锁(相邻的读读共享,读写互斥), 它的锁都支持公平和非公平两种模式 ;内部有一个队列,fifo,格式大概是,有一个head指向队首,一个tail指向队尾,具体结构如下图;条件变量实现等待唤醒机制(我也不太懂,没用过);内部有一个thread exclusiveOwnerThread记录持有锁的线程;
-
其主要是实现的功能:(这个就是一些概念,知道就行)
-
功能
1、实现阻塞获取锁 acquire 拿不到锁就去阻塞 等待锁被释放再次获取锁
2、实现非阻塞尝试获取锁 tryAcquire 拿不到锁则直接放弃
3、实现获取锁超时机制
4、实现通过打断来取消
5、实现独占锁及共享锁
6、实现条件不满足的时候等待
-
-
自定义实现AQS框架:如下图感觉没啥意思,也没有可重入的实现
ReentrantLock的加锁和解锁流程
-
首先看他和AQS关系:关系如下,图中包含公平锁,非公平锁
-
看nonfairSync的加锁过程:以下面的例子为例,大概情况为(//这里少了排队中队列元素的属性,Node.waitStatus,)
- 首先t1进来加锁,此时无竞争
- ti拿到了锁t2来了
- t1释放了锁,t2开始拿锁
- t1拿到了锁,t2t3在排队
-
线程打断:线程存在一个中断标记,这个标记的含义是记录自己是否被别的线程中断过,清除既标记制为false;那如何看这个标记呢t1.interrupted(),具体代码如下
这段英文是介绍说这个方法被其他线程中断时会调用这个方法
这段介绍的是当前线程以独占锁形式被中断,如果中断则终止,否则继续尝试拿锁,失败排队
拿锁失败后的过程,排队拿锁
-
看解锁流程,先看几张图,大致流程如下ti持有锁,t2排队,t1释放锁,t2持有锁,这里还有一个Node。waitStatus的状态。当状态为-1,作用是队首节点拿到锁后是否唤醒下一个节点
-
读读并发需注意的问题:这个是别人写的例子,证明读读并发,读写互斥
-
读写锁之写锁上锁过程:由于读写互斥,多以写锁加锁时要么锁没有别人持有,或者锁重入才可以加上锁;关于锁的数据结构,比如32bit表示锁,前16位为读锁,后16位为写锁;
具体流程如下:reentrantLock的writeLock
这里直接从tryAcqurie方法开始,首先获取当前线程,或许锁的状态state,获取写锁的状态(后16位),如果state不等于0,要判断当前是不是写锁,如果w==0则代表没上过写锁,只是读锁,直接返回false,如果是写锁则判断占有这把锁的是不是当前线程,如果托不是则返回false;如果这个判断过了则判断重入次数是否超过最大限制,如果没有就设置state,返回true;如果state=0则是第一次来,那就要区分是否是公平锁还是非公平锁,如果非公平则直接拿锁,公平则排队,如果队里没人则直接拿锁,如果队里有人则加锁失败返回false -
读写锁之读锁上锁过程
具体流程如下:reentrantLock之读锁上锁
首先获取当前线程,当前锁状态state,如果exclusiveCount(C)!=0代表是写锁,并且独占锁还不是当前线程,则返回-1;如果上诉情况没有发生则看上锁次数,如果上锁次数小于最大值,并且不排队,并且cas(c,c+share_unit)如果都满足则进入读锁加锁判定中,在这里如果读锁r=0,则代表第一个线程第一次加锁,如果读锁r!=0,但是第一个读锁线程是当前线程,或者相邻的读锁进入,都返回1 -
关于抢锁的时候非公平抢了几次锁
第一次:使用无阻塞的cas去抢锁,第二次,如对之前如何上一个节点是head则再抢一次,抢不到就去排队