尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
AQS(二):独占式锁
1、AQS属性以前文章提到过看一个类的属性这个类整个的功能。AQS中的属性public abstract class AbstractQueuedSynchronizer{ private transient volatile Node head; private transient volatile Node tail; private volatile int state; }state取值//节点从同步队列中取消 int CANCELLED 1 //后继节点的线程处于等待状态如果当前节点释放同步状态会通知后继节点使得后继节点的线程能够运行 int SIGNAL -1 //当前节点进入等待队列中 int CONDITION -2 //表示下一次共享式同步状态获取将会无条件传播下去 int PROPAGATE -3 //初始状态 int INITIAL 0;AQS的静态内部类Nodepublic abstract class AbstractQueuedSynchronizer{ static final class Node { //标记表示节点正在共享模式中等待 static final Node SHARED new Node(); //标记表示节点正在独占模式下等待 static final Node EXCLUSIVE null; //节点状态 volatile int waitStatus //当前节点/线程的前驱节点 volatile Node prev //当前节点/线程的后继节点 volatile Node next; //加入同步队列的线程引用 volatile Thread thread; //等待队列中的下一个节点和Condition配合使用 Node nextWaiter; } }剩下的方法都是围绕以上属性所服务的AQS提供的主要方法getState():返回同步状态的当前值setState(int newState):设置当前同步状态compareAndSetState(int expect, int update):使用CAS设置当前状态该方法能够保证状态设置的原子性isHeldExclusively():当前同步器是否在独占式模式下被线程占用一般该方法表示是否被当前线程独占acquire(int arg):独占式获取同步状态如果当前线程获取同步状态成功则由改方法返回否则将会进入同步队列等待该方法将会调用可重写的tryAcquire(int arg)方法acquireInterruptibly(int arg):与acquire(int arg)相同但是该方法响应中断当前线程为获取到同步状态而进入到同步队列中如果当前线程被中断则该方法抛出InterruptedException异常并返回acquireShared(int arg)共享式获取同步状态如果当前线程未获取到同步状态将会进入同步队列等待与独占式的主要区别是在同一时刻可以有多个线程获取到同步状态acquireSharedInterruptibly(int arg)共享式获取同步状态响应中断release(int arg)独占式释放同步状态该方法会在释放同步状态之后将同步队列中第一个节点包含的线程唤醒releaseShared(int arg)共享式释放同步状态tryAcquire(int arg):独占式获取同步状态获取同步状态成功后其他线程需要等待该线程释放同步状态才能获取同步状态tryRelease(int arg):独占式释放同步状态tryAcquireNanos(int arg, long nanos):超时获取同步状态如果当前线程在nanos时间内没有获取到同步状态那么将会返回false已经获取则返回truetryAcquireShared(int arg):共享式获取同步状态返回值大于等于0表示获取成功否则获取失败tryReleaseShared(int arg):共享式释放同步状态tryAcquireSharedNanos(int arg, long nanosTimeout)共享式获取同步状态增加超时限制2、独占式锁2.1、获取独占式锁调用lock()方法是获取独占式锁获取失败就将当前线程加入同步队列成功则线程执行。而lock()方法实际上会调用AQS的acquire()方法acquire根据当前获得同步状态成功与否做了两件事情成功则方法结束返回失败则先调用addWaiter()将线程加入等待队列,然后在调用acquireQueued()方法:排队再次获取锁。public final void acquire(int arg) { //获取同步状态成功则直接返回。若失败则先调用addWaiter()方法再调用acquireQueued()方法 if (!tryAcquire(arg) acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } }2.2、获取独占式锁态失败:做入队操作private Node addWaiter(Node mode) { // 1. 将当前线程构建成Node Node node new Node(Thread.currentThread(), mode); // 2. 当前尾节点是否为null Node pred tail; if (pred ! null) { // 当前同步队列尾节点不为null则将当前节点尾插入的方式插入同步队列中 node.prev pred; if (compareAndSetTail(pred, node)) { pred.next node; return node; } } // 当前同步队列尾节点为null说明当前线程是第一个加入同步队列进行等待的线程 enq(node); return node; } private Node enq(final Node node) { //自旋的过程for (;;) for (;;) { Node t tail; if (t null) { //1. 构造头结点 if (compareAndSetHead(new Node())) tail head; } else { // 2. CAS操作设置尾节点CAS操作失败后负责自旋进行尝试会在for (;;)for死循环中不断尝试直至成功return返回为止。 node.prev t; if (compareAndSetTail(t, node)) { t.next node; return t; } } } }在同步队列中的节点线程会做什么事情了来保证自己能够有机会获得独占式锁了自旋acquireQueued2.3、入队列后线程排队获取独占式锁acquireQueuedfinal boolean acquireQueued(final Node node, int arg) { boolean failed true; try { boolean interrupted false; for (;;) { // 获取当前节点的先驱节点 final Node p node.predecessor(); // 2.1 如果当前节点的先驱节点是头结点并且成功获取同步状态即可以获得独占式锁 if (p head tryAcquire(arg)) { //获取锁成功出队操作队列头指针用指向当前节点 setHead(node); //释放前驱节点 p.next null; failed false; return interrupted; } // 2.2 获取锁失败线程进入等待状态等待获取独占式锁 if (shouldParkAfterFailedAcquire(p, node) parkAndCheckInterrupt()) //如果等待过程中被中断过就将interrupted标记为true interrupted true; } } finally { if (failed) cancelAcquire(node); } } //做出队操作 private void setHead(Node node) { head node; node.thread null; node.prev null; } //就进入waiting状态直到被unpark() private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { //拿到前驱的状态 int ws pred.waitStatus; if (ws Node.SIGNAL) //如果已经告诉前驱拿完号后通知自己一下那就可以安心休息了 return true; if (ws 0) { /* * 如果前驱放弃了那就一直往前找直到找到最近一个正常等待的状态并排在它的后边。 * 注意那些放弃的结点由于被自己“加塞”到它们前边它们相当于形成一个无引用链稍后就会被GC回收 */ do { node.prev pred pred.prev; } while (pred.waitStatus 0); pred.next node; } else { //如果前驱正常那就把前驱的状态设置成SIGNAL告诉它拿完号后通知自己一下。有可能失败人家说不定刚刚释放完呢 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; } private final boolean parkAndCheckInterrupt() { //调用park()使线程进入waiting状态 LockSupport.park(this); //如果被唤醒查看自己是不是被中断的 return Thread.interrupted(); }2.4、释放独占式锁release()public final boolean release(int arg) { if (tryRelease(arg)) { Node h head;//找到头结点 if (h ! null h.waitStatus ! 0) unparkSuccessor(h);//唤醒等待队列里的下一个线程 return true; } return false; } //释放锁 protected boolean tryRelease(int arg) { throw new UnsupportedOperationException(); } //唤醒等待队列里的下一个线程 private void unparkSuccessor(Node node) { //当前线程所在的结点状态 int ws node.waitStatus; if (ws 0)//置零当前线程所在的结点状态允许失败。 compareAndSetWaitStatus(node, ws, 0); 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)//从这里可以看出0的结点都是还有效的结点。 s t; } if (s ! null) LockSupport.unpark(s.thread);//唤醒 }
RELATED

相关推荐

研一快速产出SCI论文:AI领域可执行的四步标准化流程

研一快速产出SCI论文:AI领域可执行的四步标准化流程

1. 先搞清楚“水一篇”和“能毕业”之间的真实距离先说结论:如果你被导师放养,想在研一就快速搞定一篇能毕业的论文,尤其是想投SCI,核心不是找“捷径”,而是建立一套可执行、可验证、风险可控的流程。很多人一上来就找…

📅 2026/9/18 9:22:25
MySQL安装全攻略:从手动部署到Docker容器化部署详解

MySQL安装全攻略:从手动部署到Docker容器化部署详解

你肯定遇到过这种情况:刚拿到一台新电脑,或者准备开始一个新项目,第一件事就是装环境。Java 要配 JDK,Python 要配环境变量,而数据库,十有八九是 MySQL。网上教程铺天盖地,从“一键安装”到“保姆级教程”,看似简单,但新手跟着做,总会在某个意想不到的环节卡住——环…

📅 2026/9/8 1:09:13
前端转AI Agent开发,焦虑迷茫?真相是:你的经验是核心资产,机会比想象中更大!

前端转AI Agent开发,焦虑迷茫?真相是:你的经验是核心资产,机会比想象中更大!

做了 7 年前端,去年开始转 AI Agent 开发,到现在刚好 1.5 年。 这段时间,我经历了从焦虑、迷茫,到真正上手;从自己摸索,到拿到 offer,再到开始带项目。一路踩过很多坑,也慢慢想明白了…

📅 2026/8/24 14:52:09
MORE NEWS

更多资讯

📰

人授权、AI执行:基于BIP 6的智能体工程化实践

如果你也管着一个几十人的团队,一定体会过每天被“派活”支配的感觉:任务从聊天窗口里来,在表格里传递,在口头确认中丢失。半年前,我们团队把“派活”这件事搬进了BIP 6平台,逐步搭起一套“人授权、AI执行”…

📰

抖音无水印批量下载安装教程:从克隆仓库到跑通第一个任务

抖音无水印批量下载安装教程:从克隆仓库到跑通第一个任务 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback su…

📰

动态网页仪表盘不自动刷新?TaoToken 这样改 Codex 的排查路径再对照 glue_get_state

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Altium Designer高效快捷键指南:从原理图到PCB布线提速技巧

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

制造业AI落地施工图:工业互联网+边缘智能实施指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

PDF压缩原理与场景化实战:保真、降体积、不丢功能

1. 这不是“随便找个PDF压缩网站就行”的事——为什么90%的人压完PDF反而更卡、更糊、打不开你搜“压缩pdf免费工具”,页面刷出来几十个带“极速”“秒压”“无损”字样的网站,点进去上传文件,等30秒,下载回来——结果字体发虚、表…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬