多线程-aqs与reentrantLock

AQS和ReentrantLock

ReentrantLock的lock特点

  1. 异常的锁是包含死锁和活锁,死锁无法解决,活锁可以通过错开执行时间解决

  2. lock的特点:可打断,可重入,可设置超时时间,可设置锁类型(公平非公平),支持多变量,支持锁类型(读,写)

  3. 以上都是基础概念,例子就不写了,至于多变量condition,这个我也没用过,大概意思,用对应的condition去唤醒,下面是例子

    1684118027772

AQS框架的样子

  1. AbstractQueuedSychronizer 同步队列,他有一个属性state表示锁的状态,数字表示获得锁的次数,利用cas改变其数值;其中有俩吧锁,独占锁,共享锁(相邻的读读共享,读写互斥), 它的锁都支持公平和非公平两种模式 ;内部有一个队列,fifo,格式大概是,有一个head指向队首,一个tail指向队尾,具体结构如下图;条件变量实现等待唤醒机制(我也不太懂,没用过);内部有一个thread exclusiveOwnerThread记录持有锁的线程;

  2. 1684200284575

  3. 其主要是实现的功能:(这个就是一些概念,知道就行)

    1. 功能

      1、实现阻塞获取锁 acquire 拿不到锁就去阻塞 等待锁被释放再次获取锁

      2、实现非阻塞尝试获取锁 tryAcquire 拿不到锁则直接放弃

      3、实现获取锁超时机制

      4、实现通过打断来取消

      5、实现独占锁及共享锁

      6、实现条件不满足的时候等待

  4. 自定义实现AQS框架:如下图感觉没啥意思,也没有可重入的实现

  5. 1684200741843

ReentrantLock的加锁和解锁流程

  1. 首先看他和AQS关系:关系如下,图中包含公平锁,非公平锁

  2. 1684200107138

  3. 1684200841086

  4. 1684200891149

  5. 看nonfairSync的加锁过程:以下面的例子为例,大概情况为(//这里少了排队中队列元素的属性,Node.waitStatus,)

    1. 首先t1进来加锁,此时无竞争
    2. ti拿到了锁t2来了
    3. t1释放了锁,t2开始拿锁
    4. t1拿到了锁,t2t3在排队

    1684201072553

    1684201083743

    1684201133043

    1684201181572

    16842011977751684201233632

  6. 线程打断:线程存在一个中断标记,这个标记的含义是记录自己是否被别的线程中断过,清除既标记制为false;那如何看这个标记呢t1.interrupted(),具体代码如下

    这段英文是介绍说这个方法被其他线程中断时会调用这个方法

    1684202380061

    这段介绍的是当前线程以独占锁形式被中断,如果中断则终止,否则继续尝试拿锁,失败排队

    1684202513179

    拿锁失败后的过程,排队拿锁

    1684203057541

  7. 看解锁流程,先看几张图,大致流程如下ti持有锁,t2排队,t1释放锁,t2持有锁,这里还有一个Node。waitStatus的状态。当状态为-1,作用是队首节点拿到锁后是否唤醒下一个节点

    1684203438821

    1684203444714

  8. 读读并发需注意的问题:这个是别人写的例子,证明读读并发,读写互斥

    1684204149885

  9. 读写锁之写锁上锁过程:由于读写互斥,多以写锁加锁时要么锁没有别人持有,或者锁重入才可以加上锁;关于锁的数据结构,比如32bit表示锁,前16位为读锁,后16位为写锁;

    具体流程如下:reentrantLock的writeLock
    这里直接从tryAcqurie方法开始,首先获取当前线程,或许锁的状态state,获取写锁的状态(后16位),如果state不等于0,要判断当前是不是写锁,如果w==0则代表没上过写锁,只是读锁,直接返回false,如果是写锁则判断占有这把锁的是不是当前线程,如果托不是则返回false;如果这个判断过了则判断重入次数是否超过最大限制,如果没有就设置state,返回true;如果state=0则是第一次来,那就要区分是否是公平锁还是非公平锁,如果非公平则直接拿锁,公平则排队,如果队里没人则直接拿锁,如果队里有人则加锁失败返回false

    1684204254689

  10. 读写锁之读锁上锁过程

    具体流程如下:reentrantLock之读锁上锁
    首先获取当前线程,当前锁状态state,如果exclusiveCount(C)!=0代表是写锁,并且独占锁还不是当前线程,则返回-1;如果上诉情况没有发生则看上锁次数,如果上锁次数小于最大值,并且不排队,并且cas(c,c+share_unit)如果都满足则进入读锁加锁判定中,在这里如果读锁r=0,则代表第一个线程第一次加锁,如果读锁r!=0,但是第一个读锁线程是当前线程,或者相邻的读锁进入,都返回1

    1684204276642

  11. 关于抢锁的时候非公平抢了几次锁

    第一次:使用无阻塞的cas去抢锁,第二次,如对之前如何上一个节点是head则再抢一次,抢不到就去排队

posted @ 2023-05-16 11:00  小傻孩丶儿  阅读(17)  评论(0编辑  收藏  举报