发布于2022-07-08
是什么
AQS就是指的是JDk中的AbstractQueuedSynchronizer类,字面义是就是抽象队列同步器,是用来构建锁或者其它同步器组件的重量级基础框架及整个JUC体系的基石 那些锁的实现基本都用到了他。
他通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int类变量表示持有锁的状态
和他有关的锁
ReentrantLock,CountDownLatch,ReentrantReadWriteLock,Semaphore等等。基本都是这些锁类的内部有一个名称为Sync的内部类去继承这个AQS这个抽象类,并重写调用AQS类中的一些方法来实现自身的功能。
能做什么
抢到资源的线程直接使用处理业务,抢不到资源的必然涉及一种排队等候机制。抢占资源失败的线程继续去等待(类似银行业务办理窗口都满了,暂时没有受理窗口的顾客只能去候客区排队等候),但等候线程仍然保留获取锁的可能且获取锁流程仍在继续(候客区的顾客也在等着叫号,轮到了再去受理窗口办理业务)。
既然说到了排队等候机制,那么就一定会有某种队列形成,这样的队列是什么数据结构呢?
如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的,将暂时获取不到锁的线程加入到队列中,这个队列就是AQS的抽象表现。它将请求共享资源的线程封装成队列的结点(Node),通过CAS、自旋以及LockSupport.park()的方式,维护state变量的状态,使并发达到同步的效果。
初步解读
官网解释
AQS使用一个volatile的int类型的成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作将每条要去抢占资源的线程封装成一个Node节点来实现锁的分配,通过CAS完成对State值的修改。
AQS内部体系架构
AQS的int变量
AQS的同步状态State成员变量,0表示这个锁没有线程占用,1表示正在被占用 通过cas去修改该值
/**
* The synchronization state.
*/
private volatile int state;
AQS的CLH队列(fifo)
CLH队列(三个大牛的名字组成),为一个双向队列,注意他是先进先出的队列
Node内部类
Node的int变量
Node的等待状态waitState成员变量,表示其他线程的等待状态 注意队列最后的node节点这个状态永远为0
volatile int waitStatus
属性说明
aqs图示
从ReentrantLock解读AQS
类图
公平锁与非公平锁的区别
可以明显看出公平锁与非公平锁的lock()方法唯一的区别就在于公平锁在获取同步状态时多走了一个方法hasQueuedPredecessors(),他是公平锁加锁时判断等待队列中是否存在有效节点的方法,如果已经存在节点了,公平锁会去直接排队,非公平锁会直接尝试去抢占锁,但是一旦线程进入了fifo队列,就会遵循先进先出的原则,要有先来后到。
非公平锁源码解读
lock方法
final void lock() {
//直接上来就尝试抢占
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//抢占不到再去尝试或者进入fifo队列
acquire(1);
}
acquire方法
tryAcquire方法
这里注意一个重点 当current == getExclusiveOwnerThread()时表示是重入进来的锁,这时候会一直把state变量+1加上去,后面释放的时候也会一直减到0 这里就是为了支持可重入锁,否则你自己写个递归,然后自己把自己锁死了不就成了笑话了.
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//判断如果锁已经没有线程占用了 直接去尝试再次抢占
if (compareAndSetState(0, acquires)) {
//设置当前线程为自己
setExclusiveOwnerThread(current);
return true;
}
}
//如果锁正在被占用判断占用锁的线程是不是自己
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
addWaiter(Node mode)
双向链表中,第一个节点为虚节点(也叫哨兵节点),其实并不存储任何信息,只是占位。真正的第一个有数据的节点,是从第二个节点开始的。
//新建立了一个用来占位的节点 此时头指针和尾指针都指向这个node节点
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
后面再有线程进来
调用compareAndSetTail
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
final boolean acquireQueued(final AbstractQueuedSynchronizer.Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final AbstractQueuedSynchronizer.Node p = node.predecessor();
//再去尝试下能不能抢占到锁 抢占不到就算了
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//再次抢占失败进入 更改节点状态的方法 然后再去阻塞线程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire和parkAndCheckInterrupt方法
private static boolean shouldParkAfterFailedAcquire(AbstractQueuedSynchronizer.Node pred, AbstractQueuedSynchronizer.Node node) {
//获取前驱节点的状态
int ws = pred.waitStatus;
if (ws == AbstractQueuedSynchronizer.Node.SIGNAL)
//如果是SIGNAL状态,等待被占用的资源释放,直接返回true
//准备继续调用parkAndCheckInterrupt方法
return true;
//ws大于0说明是CANCELLED状态
if (ws > 0) {
//循环判断前驱节点是否也为CANCELLED状态忽略掉该状态的节点,重新连接队列
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//将当前节点设置为SIGNAL状态,用于后续唤醒操作
//程序第一次执行到这返回为false,还会进行外层第二次循环,最终从第一个if返回
compareAndSetWaitStatus(pred, ws, AbstractQueuedSynchronizer.Node.SIGNAL);
}
return false;
}
private final boolean parkAndCheckInterrupt() {
//直接挂起线程 程序不会继续向下执行了
LockSupport.park(this);
//根据LockSupport的用法 程序在三种情况下会被唤醒继续执行
//1.被unpark
//2.被中断
//3.其他不合逻辑的返回 异常等
//如果被唤醒了 返回当前线程的中断状态,并清空中断状态
// 如果是被中断的 该方法会返回true
return Thread.interrupted();
}
unLock方法
- sync.release(1) 释放当前锁的占用线程,然后将锁占用标志state更新为0
unparkSuccessor(h) 判断有头结点并且头结点的waitStatus不为0 说明有后续节点,再判断后续节点不为null并且后续节点的等待状态正常,如果不正常就把下一个节点指向头结点直到找到能够被正常唤醒的节点,然后调用LockSupport.unpark(s.thread)唤醒
private void unparkSuccessor(Node node) { /* * If status is negative (i.e., possibly needing signal) try * to clear in anticipation of signalling. It is OK if this * fails or if status is changed by waiting thread. */ int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); /* * Thread to unpark is held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor. */ Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) LockSupport.unpark(s.thread); }
Natalieay fotballdrakter LoganBisc MarshaGop brasil VM Drakt RobinMulv AlizaBeeb mexico VM tröja PhilippDo Kenbahpg real madrid trøjer ManieRabi EleanorLy VM fotbollstrojör VM BernardoE AguedaDun maglia atalanta 2023 CornellSi BeatrisAt coutinho drakt barn Jonnautxv ShelleyFa manchester united tøj PenelopeV DaltonPoi frankrike VM Drakt Linwoodil UlrikeMej fotballdrakter AmyPanton OsvaldoRi arsenal trøje RichieBon GaleLower chelsea drakt FlynnChun CarmeloWi Maglia Chile Mondiali 2022 RosaPanne MabelWind leeds drakt GlindaSer MariaStol maglia italia portiere Beulahyxy SusieMich marseille tröja TraceyMew AguedaDun maglia atalanta 2023 CornellSi CheriRoth arsenal trøje MonteBall Lashawnda manchester united tröja barn GarryKerr Torstenib Kroatien VM 2022 Landsholdstrøje ArielleHa
Ramonitah albanien VM Drakt SteveCawt CaraCastl Maglia Brazil Mondiali 2022 BellaMoul DedraHjee osterrike VM Drakt WoodrowSt EmeryStac Maglia Mexico Mondiali 2022 JaredWess CaraCastl Maglia Brazil Mondiali 2022 BellaMoul OdellWayl psg trøje børn HalinaDup Raymundoq nigeria VM 2022 Drakt Demetrius CathrynHw VM fotbollstrojör VM Maximoffd BeulahMer real madrid fodboldtøj BreannaHo WilsonmtF sydkorea VM tröja DZIEmely CrystalDr maglia manchester united 2023 BarbaraIv AltaLawle maglie svizzera ChiSatter MariaTrap kroatia VM 2022 Drakt AudraSumm NicoleDos polen VM tröja JosefamgH ToniaciY Maglia Slovenia Mondiali 2022 LudieCunn MerrillRe OLKTreydx PrestonBo juventus trøje børn DelBloomf Hannelore psg tröjor LloydFais Temekazlr Maglia Slovenia Mondiali 2022 Rebbecacs StewartBl Maglia Belarus Mondiali 2022 Alexandra
你大概是什么步骤能描述一下吗?设置了nginx的静态链接了吗?
Araofclmh australia vm barn Drakt KaseyTls Kristieif real madrid drakt ThadHilly RhysHorns liverpool tröja barn DedraNett AlissaGal Maglia Chile Mondiali 2022 MilanFait OnitaSass chelsea drakt AlannahEa NaomiLev liverpool tröja barn JuanTregu AdrieneBe Brasilien VM 2022 Landsholdstrøje MilfordNe SimonJsh maglie napoli 2022 JuliaLayt EzraPouli marseille tröja LinoldDi JannieSpr chelsea matchtröja GlendaMes CraigTasm Atalanta Drakt LenaBella LashondaB Maglia Armenia Mondiali 2022 JermaineE RicoLeona danmark VM tröja Shananwsw Gildaohft VM fotbollstrojör VM CaryBrans TrenavcZ Portugal VM 2022 Landsholdstrøje JewelJano Jacquetta Island VM 2022 Landsholdstrøje FreyaYdyr TobiasHer Portugal VM 2022 Landsholdstrøje AzucenaVe BeatriceV australia VM 2022 Drakt VeronaTul MoseButtr Maglia Wales Mondiali 2022 StacyHytt TobiasHer Portugal VM 2022 Landsholdstrøje AzucenaVe
我发现我的评论功能,加载不了头像!朋友圈和相册也加载不了,音乐播放器也不显示,求解!