标签: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()
未完待续...
标签:for 过程 ued 结构 abs Owner inf 图片 重点
原文地址:https://www.cnblogs.com/ttaall/p/13828134.html