JUC_02 AQS工作原理

2021/5/12 10:33:41

本文主要是介绍JUC_02 AQS工作原理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

image

AQS: state + chl队列完成

Lock.lock()

判断state状态:compareAndSetState

判断state值是否等于0,如果等于0,
说明当前还没有线程获取锁对象资源。
compareAndSetState(0, 1):将state值改为1,
setExclusiveOwnerThread:将当前线程设置为排它线程,其它线程获取锁资源需要等待

获取锁:acquire

acquire(arg) {
if (!tryAcquire(arg) && acquireQueued(
addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}

尝试获取锁:tryAcquire

tryAcquire:
1、判断当前state值是否为0,若是,则并把当前线程设置为排它线程并直接给当前线程放行,
2、判断当前线程是否和排它线程是同一个线程,若是,则将state值进行累加并直接给当前线程放行

添加等待者:addWaiter

1、判断tail节点是否为空
Node pred = tail;
若不为空,则将新加入的node.prev指向pred,再将tail节点改为node节点,最后将pred.next指向node节点

若为空,则执行enq方法(自旋)
第一次:
tail为空:
创建一个新节点作为哨兵节点node
将head节点指向node,然后将
tail节点指向head。
第二次:
tail不为空:
Node t = tail;
将node.prev指向t,
将tail指向node,
最后将t.next指向node

将当前线程封装成Node节点,判断tail节点是否为空,若为空,则指向enq方法(创建哨兵节点),若不为空,直接拼接后续节点。

加入队列:acquireQueued

1、先获取当前节点的前序节点:Node p = node.predecessor(),
若p==head且tryAcquire(arg)为true时,
将当前节点作为head节点,且p.next=null(GC回收)

若不满足,则继续执行:shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()

获取锁成功后将node节点作为head节点,head.next置为nul(GC回收)

final Node p=node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}

获取锁失败后进行阻塞操作:shouldParkAfterFailedAcquire && parkAndCheckInterrupt

一、shouldParkAfterFailedAcquire
1、判断Node的waitStatus状态,
将pred节点的waitStatus设置为Node.SIGNAL对应值,用于指示线程执行unpark操作
二、parkAndCheckInterrupt
1、LockSupport.park(this);
将当前线程进行阻塞

Lock.unlock

释放锁:release

尝试释放锁:tryRelease

1、判断当前线程是否是获取锁的排它线程,若不是则抛出异常:IllegalMonitorStateException
2、判断state值是否为0,若等于0则将排它线程置为null
3、更新state状态值

唤醒队列中的节点:unparkSuccessor

一、判断head节点是否为空且waitStatus值是否为0,满足条件:
1、将该节点的waitStatus值重置为0
2、获取下一个节点,若下一个节点s不为空,
则执行LockSupport.unpark(s.thread);
从此唤醒s节点的线程。让它尝试继续获取锁操作

更改waitStatus值状态:compareAndSetWaitStatus
唤醒阻塞线程:unpark(s.thread)


这篇关于JUC_02 AQS工作原理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程