码迷,mamicode.com
首页 > 其他好文 > 详细

ReentratLock源码分析,AQS原理解析

时间:2020-10-18 16:39:25      阅读:18      评论:0      收藏:0      [点我收藏+]

标签:for   过程   ued   结构   abs   Owner   inf   图片   重点   

先画个大致的假类图

技术图片

主要的类都在这里,核心就是ReentrantLock的内部类 Sync,

FairSync NonfairSync 是Sync的公平锁 非公平锁的实现

 

Sync继承于AbstractQueueSynchronizer(AQS)  核心功能也都在这 先来分析AQS

 

AQS的核心思想就是:  一个队列(实质是一个链表结构)  + 一个状态  /  a Queue and a state 

大致思路 : 

state代表当前锁的状态 默认为0,当该锁被某个线程持有则state 为1,如果是重入锁 每重入一次state+1

当线程尝试获取锁失败就将线程自己封装成Node节点加入到队列,然后挂起等待被唤醒,再去竞争锁

 

完结 !

 

源码分析:

竞争锁的队列该有的样子

技术图片

 

 

 

AQS的几个核心属性  : head, tail, state, exclusiveOwnerThread

技术图片

 

 

head 表示队列的头节点   (头节点不属于阻塞队列!头节点不代表实际的线程)

state 表示当前锁的状态  0 代表无人占有  1 代表被线程占用,每+1 代表被重入一次

tail 表示队列的尾节点

 

技术图片

 

 

 AQS继承于AbstractOwnableSynchronizer

继承了重要属性 exclusiveOwnerThread   表示当前占有锁的线程

 

AQS内部类 Node

技术图片

 

 

 几个重要的属性:

next 后继节点,

perv 先驱节点,

thread 该节点代表的线程,

waitStatus 表示后继节点是否需要唤醒 (由后继节点控制,默认是0 不需要唤醒,当后继节点需要被前置节点唤醒则将0设置为-1,如果前置节点主动放弃竞争锁 则将waitStatus > 0)

EXCLUSIVE 标识节点会独占锁

SHARED 标识节点会共享锁

 

从客户端调用入手分析

加锁过程

public static void main(String[] args) {

Lock lock = new ReentrantLock();
lock.lock();
try {
//do something
}finally {
lock.unlock();
}

}

 

1.初始化  无参默认是非公平锁

技术图片

 

 

 

2.lock.lock();  非公平锁的实现

技术图片

 

 

 先试探CAS去改锁的状态,如果成功则加锁成功! 将持有锁的线程设置为自己 over!

如果CAS失败则 执行acquire(1), 参数传1

 

3. acquire  决定是否要将自己中断

技术图片

 

 

 if(尝试获取锁失败  && 将当前线程封装好了加入到阻塞队列都处理好了){

   将自己中断();

}

 

 

 

4.  重点:tryAcquire(1)  尝试去获取锁  传的参数是1

技术图片

技术图片

 

 

 非公平锁的tryAcquire()实现,

先判断state是否为0 (当前锁是否没被持有)

if (state == 0){

  if (尝试CAS直接去改变状态 因为此时可能会有多个线程同时发现了state是0 都想去改变状态){

    如果CAS成功了就将持有锁的线程设置为自己 over!

  }

}else if (当前线程就是当前持有锁的线程,说明这是锁重入 ){

  直接state+1

}

  尝试获取锁失败了 return false;

 

5.  boolean acquireQueued()   中断当前线程是否成功 , 传入封装好的节点  和参数1

技术图片

 try {

  for(;;){

    获取当前节点的前直接点 p

    if (如果p是head节点 并且 再次尝试获取锁成功了){

      就把当前节点设置为head节点

       下个节点置空

       return  中断当前线程失败

    }

    if ( 再次抢占锁失败后是否需要挂起 && 检查并挂起线程){

      中断成功

    }

  }

 

} finally {

  //failed == true的情况  再次尝试获取锁抛异常导致

  if (如果中断失败了){
    解散节点 不玩了 

  }

 

6. addWaiter()  尝试加入到阻塞队列   传入的mode标识当前节点是想 独占锁 还是想共享锁

技术图片

 

 

将当前线程封装成一个Node

获取到尾节点 pred

if (如果尾节点不为null){

  将perd作为当前节点的前置节点

  if (CAS将当前节点设置为队列的尾节点){

    return 当前节点;

  }

}

技术图片

说明尾节点是null,就说明没有head节点,

for循环{

判断尾节点是否为空,如果为空则CAS 将自己设置为head节点 兼 尾节点,

如果中途判断发现尾节点不为空了(可能是被别人抢先占了头节点了),就默默的加入到尾节点后面

}  

 

解锁过程 unlock()

 未完待续...

ReentratLock源码分析,AQS原理解析

标签:for   过程   ued   结构   abs   Owner   inf   图片   重点   

原文地址:https://www.cnblogs.com/ttaall/p/13828134.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!