范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

并发编程AQS源码解析

  在并发编程中,ReentrantLock作为一个非常重要同步组件,基层是基于AQS同步器构建,我们一起以ReentrantLock源码,分析AQS工作原理。 简介
  AQS(AbstractQueuedSynchronizer)使用一个int成员变量state表示同步状态(state为0,表示当前资源没有被占用,state>0,表示当前资源已经被占用),结合内置的一个同步队列(FIFO)完成资源获取线程的等待排队工作,如果当前线程没有获取到锁,则将线程封装成一个Node节点,加入到同步队列,等待唤醒,通过自旋的方式尝试获得锁。
  Node节点组成 waitStatus:等待状态
  Node prev:前驱节点
  Node next:后继节点
  Thread thread:获取同步状态的线程
  Node nextWaiter:等待队列中的后继节点
  Node节点状态:waitStatus INITIAL=0:初始状态
  SIGNAL=-1:后继节点的线程处于等待状态,当前节点如果释放了同步状态,将通知后继节点
  CONDITION=-2:节点在等待队列中,节点线程等待在Condition上,当其他线程对Condition调用了signal方法后,该节点将从等待队列转移到同步队列中,加入到对同步状态的获取中
  PROPAGATE=-3:表示下一次共享式同步状态获取将会无条件的被传播下去
  CANNELED=1:在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消等待,节点进入该状态将不会变化源码分析
  1. 加锁
  首先从ReentrantLock的lock方法开始。 public void lock() {     sync.lock(); }
  可以发现ReentrantLock实际调用的sync对象的lock方法,sync对象是ReentrantLock内部的一个静态内部类。 abstract static class Sync extends AbstractQueuedSynchronizer {         // ... }
  sync类有两个是实现:FairSync、NonfairSync,分别对应公平锁和非公平锁。 先从非公平锁的角度分析源码,下文用sync代替非公平锁NonfairSync。  final void lock() {     if (compareAndSetState(0, 1))         setExclusiveOwnerThread(Thread.currentThread());     else         acquire(1); }
  sync类的lock方法先尝试通过CAS修改state的状态位1,如果修改成功,表示当前线程获取到锁。否则调用acquire方法尝试加锁。 public final void acquire(int arg) {     if (!tryAcquire(arg) &&         acquireQueued(addWaiter(Node.EXCLUSIVE), arg))         selfInterrupt(); }
  acquire方法内部再次尝试通过tryAcquire获取锁。 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; }
  从这个方法可以看出,如果当前线程已经拥有锁,则将状态+acquires(获取锁的次数,需要释放相同的次数,可重入)直接返回加锁成功。如果没有获取到锁,则返回false。
  回到acquire方法,假设tryAcquire没有加锁成功,继续跟踪addWaiter,通过方法名称可以猜出来,这个方法是将当前线程封装成一个Node。 private Node addWaiter(Node mode) {     Node node = new Node(Thread.currentThread(), mode);     // Try the fast path of enq; backup to full enq on failure     Node pred = tail;     if (pred != null) {         node.prev = pred;         if (compareAndSetTail(pred, node)) {             pred.next = node;             return node;         }     }     enq(node);     return node; }
  首先将当前线程封装成一个Node对象,如果当前队列是空的情况下,head和tail默认是都null,if (pred != null) 条件不成立,继续跟踪enq方法。  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;             }         }     } }
  第一次循环,t为null,if (t == null) 条件成立,通过CAS设置head节点为一个空的Node节点(哨兵节点),然后让tail和head同时指向哨兵节点。
  哨兵节点表示当前已经获得锁,正在执行业务逻辑的线程
  第二次循环,t不为null(t指向哨兵节点),node(当前线程封装的对象)的前驱指针指向t,即第一次循环创建的哨兵节点,通过CAS让tail指向node节点,然后将哨兵节点的后继指针指向node节点,结束循环。
  通过自旋的方式初始化了哨兵节点,并且将当前线程Node节点添加到同步队列中,假设此时有多个线程抢占锁,最后都会进入同步队列等待。
  继续跟踪acquireQueued方法,首先获取当前线程Node节点的前驱节点p,判断前驱节点p是否是头节点。通过上面的分析可知,p是头结点,然后通过通过tryAcquire方法尝试获取锁。 如果加锁成功,则将当前节点设置为头结点,并将之前创建的哨兵节点的next指针指向null,即断开哨兵节点与当前线程Node节点之间的关联,此时当前线程已经获取到锁。 如果加锁失败,则调用shouldParkAfterFailedAcquire方法。 final boolean acquireQueued(final Node node, int arg) {     boolean failed = true;     try {         boolean interrupted = false;         for (;;) {             final 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);     } }
  头结点当前等待状态是初始状态,所以进入else代码块,设置头结点状态为SIGNAL(-1),继续进行下一次循环。 假设下一次循环依旧没有加锁成功,再次进入shouldParkAfterFailedAcquire方法,当前头结点状态已经变成SIGNAL,返回true,继续调用parkAndCheckInterrupt方法。 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {     int ws = pred.waitStatus;     if (ws == Node.SIGNAL)         return true;     if (ws > 0) {         do {             node.prev = pred = pred.prev;         } while (pred.waitStatus > 0);         pred.next = node;     } else {         compareAndSetWaitStatus(pred, ws, Node.SIGNAL);     }     return false; }
  可以看出,当前线程被阻塞,进入等待状态。
  至此已经完成了线程加锁失败,进入同步队列排队等待源码分析,下面继续看等待线程如何被唤醒。 private final boolean parkAndCheckInterrupt() {         LockSupport.park(this);         return Thread.interrupted(); }
  2. 释放锁
  排队线程的唤醒必须要由其他线程释放锁触发,所以我们继续跟踪ReentrantLock的unlock方法。 public void unlock() {     sync.release(1); }public final boolean release(int arg) {     if (tryRelease(arg)) {         Node h = head;         if (h != null && h.waitStatus != 0)             unparkSuccessor(h);         return true;     }     return false; }
  ReentrantLock通过Sync的release方法释放锁,release调用了tryRelease方法,跟踪tryRelease方法源码。
  判断当前线程是否是锁的独占线程,如果不是,抛出异常。所以我们使用ReentrantLock的时候,必须要加锁成功,才能执行unlock方法。 如果state-releases的结果等于0,说明已经释放锁成功,清空当前锁的独占线程,重置状态为0。 这里必须要注意,我们加锁几次,就要释放几次。  protected final boolean tryRelease(int releases) {     int c = getState() - releases;     if (Thread.currentThread() != getExclusiveOwnerThread())         throw new IllegalMonitorStateException();     boolean free = false;     if (c == 0) {         free = true;         setExclusiveOwnerThread(null);     }     setState(c);     return free; }
  继续回到Sync.release方法,释放成功之后,如果头节点h不等于空,并且头结点h的等待状态不是0,调用unparkSuccessor方法(通过上面的分析可知,头结点的状态是-1,表示头结点的后继节点处于等待状态)。
  头结点h的状态等于SIGNAL(-1),CAS设置头结点状态为0,节点s(指向线程A对应的Node节点)不等于空,并且节点s的状态为初始状态(0),不会进入if条件,通过LockSupport.unpark唤醒线程A。 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)                 s = t;     }     if (s != null)         LockSupport.unpark(s.thread); }
  如果线程A等待超时或者被中断的情况
  如果节点s的状态大于0(即线程A等待超时或者被中断),执行for循环,此时tail指向线程B对应的Node节点。 第一次循环,t=tail,t指向Node B,并判断Node B的等待状态,Node B是初始状态(0),t赋值给s,s指向Node B。 第二次循环,t=Node A(t = t.prev),Node A由于等待超时或者被中断,所以等待状态waitStatus等于1,if (t.waitStatus <= 0)不成立。 第三次循环,t=head(t = t.prev),for循环条件不成立,结束循环
  至此,s指向Node B,继续往下执行,通过LockSupport.unpark唤醒线程B。

无语,谷爱凌就读斯坦福,6000美籍华人提出无耻要求,校方表态作为全球体育盛会北京冬奥会的奥运冠军,谷爱凌是中国体育的骄傲。而这位年轻人除了在体育方面是个天才外,在学业方面同样也是出类拔萃。近日,她正式进入到世界知名高校斯坦福大学就读,成为一NBA看台女神米兰达!嫁亿万富豪,快40岁魅力不减,侧颜神似肯豆在NBA赛场不仅出现了不少优秀的运动选手,还出现了许多美女看官。还记得NBA看台女神米兰达,当初的她凭借着出众的外表和姣好的身材成为了网友心目中的女神,但随着年龄的不断增长,这名女孙颖莎出任1单原因!因为与陈梦有关,大满贯已成奢望2022年成都世乒赛马上就要开始了,球迷们对于孙颖莎出任女团一单的位置,也是议论纷纷,要知道孙颖莎目前的水平和状态,已经不被大多数人所看好,甚至有球迷认为孙颖莎目前不堪重用,为什么曼联再挖角马德里核心,曼城抢巴萨意中人,姆巴佩加盟皇马仍有戏第一个是关于曼联的消息。曼联挖角马德里上瘾,有望免签顶级核心,填补最后一块夺冠拼图拥有一位出色的门将,绝对是夺冠的最大保障之一,上赛季的库尔图瓦多次救主,皇马才得以惊险地连续击败巴牛奶致癌又添新证?经常摄入乳制品的人,肝癌风险升高18前段时间,中国营养学会更新了中国居民膳食指南(2022),其中修改了奶制品的推荐摄入量,从之前的每天300g液态奶提高到奶及奶制品300500g。图片来源中国居民膳食指南(2022入秋,吃白菜韭菜不如吃它,营养极高,增强抵抗力,身体棒秋日生活打卡季入秋,吃白菜韭菜不如吃它,营养极高,增强抵抗力,身体棒!人间烟火,最抚人心,便是那简单的一日三餐大家好,我是晓苹,每天分享生活中美食,让爱走进厨房,让家充满爱。秋天到57岁大妈坦言我想找伴侣,只需满足这4个要求即可,可惜找不到每个人都想要度过幸福的一生,年轻人是这样,老年人也是这样。并不是说老年人的时光就比不上年轻人的珍贵,很多老年人甚至珍惜老年青春的点点滴滴,认为追求幸福生活,对于老年人也是必须的。尤达尼洛对阵西班牙我们只想着取胜我现在的身体状态是100直播吧9月26日讯葡萄牙将在本轮欧国联的比赛中主场迎战西班牙,赛前,葡萄牙后卫达尼洛出席了新闻发布会,他谈到了关于本场比赛的一些话题。本场比赛我们的目标是赢球去晋级最终的四强,我们没想到手机日历有隐藏功能,设置后帮了我很大的忙,不试试可惜了今天给大家分享一些手机的使用技巧,让老年朋友使用手机更加方便快捷,现在的智能手机真的是太智能化了,手机上面的功能也是越来越多,很多老年朋友都不会操作,今天这啊,我要跟大家分享一个手过敏性鼻炎朋友,讨论治病,还要养生锻炼身体过敏性鼻炎由好多症状组成,不是一个东西可以除掉,也不是止痛止痒可以除掉疼痛瘙痒。要么一起没有了,要么还是继续发作。这就是买药不管事了。有时买药管用了,这个结果就不深究,大有水分。还完美暴露身体缺陷,明星雷人红毯造型,看得我忍不住笑出了声红毯一直以来都是暗流涌动的修罗场。虽然只有短短几十米,却聚集了数不清的镜头,全方位地展现明星的魅力。虽然每一个明星都有一颗艳压全场的心,但事与愿违,不少人反而露了怯,完美暴露身体缺
长沙凌晨1点的直播一条街把我看傻了别把人生拱手交给他人01hr在网上刷到凌晨1点的长沙直播一条街,相当炸裂。挺长的一条人行道,群魔乱舞。男主播手拿着拖鞋,狠狠抽向身着粉色包臀裙的女主播,还在喊送礼!上分!一转身,另一个女主播熟练走着后赛琳娜戏剧女王与人生赢家!论欧美娱乐圈最刺激的事件之一,无非是顶流正主们亲自下场互扯头花的世纪大战。这不,前几天赛琳娜发了自己不小心把眉毛修剪过度的日常,几个小时后卡戴珊家族的金小妹凯莉就晒出与海莉聊天画面我说这是南京旅游天花板,不过分吧!一秦淮河樊川居士是这样描绘秦淮河的烟笼寒水月笼沙,夜泊秦淮近酒家。想身临其境地感受南京烟火气,只要乘着画舫夜泊秦淮一趟,就什么都懂了。二金陵小城金陵小城拥有最浪漫的建筑语言金陵蓝诗崇明这些影剧院里,藏着多少人美好的青春记忆在每个城市或地区,总有那么一个地点一个建筑,展示着当地的文化风貌,陪伴那里生活的人们,见证时代的变迁,成为一道独特的风景。我们推出崇明寻迹系列,找寻散落在崇明三岛的这些地标,发现它特变电工正泰电器宏发股份,好日子要来了吗近年来,在各类发电用电和传输端高速增长的带动下,我国输配电设备迎来了高速发展目前我国输配电设备行业,ABBAREVA西门子东芝等几大跨国集团公司以技术和管理优势形成了第一阵营,占据爱情,是人类最美好的情感之一爱情,是人类最美好的情感之一。它可以让我们感受到无尽的温暖和幸福,也可以让我们经历无尽的痛苦和悲伤。但无论如何,它都是值得我们去追求和珍惜的。我曾经遇到过一个人,他让我体会到了爱情晨曦温柔绵绵,微笑迎接新的一天春何曾说话呢?但她那伟大潜隐的力量,已这般的,温柔了世界了!冰心脸上有微笑,心中有快乐,胸怀有志气,行动有举措,生活实实在在过。早上好!晨曦温柔绵绵,微笑迎接新的一天。早安吉祥安康周末,扬州茱萸湾迎来春游潮春天是扬州最明媚的季节,春风和煦繁花似锦。今天是周末,不少市民游客结伴而行,感受扬州的盎然春意,尽享周末美好时光。上午,茱萸湾景区里,前来游玩赏春看小动物的市民游客熙熙攘攘。在景区1977年,冼恒汉被解除多个职务,表态我调走!与搭档合作不愉快1977年,在兰州工作27年的冼恒汉,被免去兰州军区党委第一书记第一政委甘肃省委第一书记省革委会主任的职务。调回军委重新分配工作,对调动工作,他当时表态拥护。可最后,没有等到新任命呼伦贝尔必去景点满洲里(附实用旅游攻略)满洲里是一座口岸城市,也是中国最大的陆运口岸城市。西邻蒙古国,北接俄罗斯,总面积453平方公里,满洲里融合中俄蒙三国文化元素,被称为东亚之窗。因东清铁路修建,汉语翻译为满洲里所谓上多人间谍游戏欺骗公司现已能免费试玩,3月21日将正式上线欺骗公司是一款充满社交潜行与诡计的多人游戏,在游戏内化身世界上最强的秘密特工潜入敌营吧。将自己伪装成路人,运用各种高科技工具掌握先机,并赶在竞争者之前取得包裹!记住,你是DECEI