安庆大理运城常德铜陵江西
投稿投诉
江西南阳
嘉兴昆明
铜陵滨州
广东西昌
常德梅州
兰州阳江
运城金华
广西萍乡
大理重庆
诸暨泉州
安庆南充
武汉辽宁

独占锁和共享锁底层原理分析

10月22日 血海塔投稿
  独占锁模式
  前面说到,ReentrantLock锁就是基于独占锁实现的,独占锁的加锁和解锁操作都是通过互斥方式实现的。
  加锁流程
  在AQS中,是通过acquire()方法来加锁的,源码如下:publicfinalvoidacquire(intarg){if(!tryAcquire(arg)acquireQueued(addWaiter(Node。EXCLUSIVE),arg))selfInterrupt();}复制代码
  首先映入眼帘的是tryAcquire(arg)方法,翻译过来是尝试获取锁的意思,由于是用连接,只有tryAcquire(arg)方法失败时才会往下执行,当tryAcquire(arg)方法成功返回true时表示获取资源成功,对于tryAcquire(arg)方法,里面没有实现具体的东西,只是抛出了一个异常,具体逻辑是由AQS的子类实现的:protectedbooleantryAcquire(intarg){thrownewUnsupportedOperationException();}复制代码
  当tryAcquire(arg)返回为false时,会往后执行addWaiter(Node。EXCLUSIVE)方法,将当前线程封装成独占模式并添加到AQS的等待队列尾部,如果当tryAcquire(arg)返回true,则会直接让当前的线程继续执行,不需要添加到等待队列尾部,点入addWaiter(Nodemode)方法,会看到如下源码:privateNodeaddWaiter(Nodemode){这个可以参考上面Node的构造方法Node(Threadthread,Nodemode){UsedbyaddWaiterthis。nextWthis。}构造新的等待线程节点NodenodenewNode(Thread。currentThread(),mode);新建临时节点pred指向尾节点N队列不为空的话,通过CAS机制将node放到队列尾部if(pred!null){将node的prev域指向尾节点node。通过CAS机制将node放到队列尾部if(compareAndSetTail(pred,node)){将原来尾节点的next域指向当前node节点,node现在为尾节点pred。形成双向链表}}如果队列为空的话enq(node);}复制代码
  在多线程并发情况下,如果有多个线程同时争夺尾节点的位置,会调用enq(node)方法,使用CAS自旋机制挂到双向链表的尾部,下面是源码:privateNodeenq(finalNodenode){死循环(自旋)for(;;){N尾节点为null,说明头结点也为null,可能是还没有创建队列的时候if(tnull){多线程并发情况下,利用CAS机制创建头结点和尾节点,CAS保证此时只有一个头节点被创建,下次自旋时,就会满足队列不为空的条件if(compareAndSetHead(newNode()))}else{如果存在尾节点,将当前节点的prev域指向尾节点node。利用CAS机制完成双向链表的绑定,让之前尾节点指向当前node节点if(compareAndSetTail(t,node)){t。}}}}复制代码
  接下来看一看compareAndSetTail方法使用CAS乐观锁机制的方法源码:privatefinalbooleancompareAndSetTail(Nodeexpect,Nodeupdate){returnunsafe。compareAndSwapObject(this,tailOffset,expect,update);}复制代码
  上述代码讨论到,使用tryAcquire(intarg)方法返回false时,再使用addWaiter(Nodemode)方法,将当前线程放入到队列的尾部。acquireQueued(finalNodenode,intarg)方法,等待休息直到其他线程唤醒finalbooleanacquireQueued(finalNodenode,intarg){拿到资源失败情况置为true,表示没有拿到资源try{用于判断线程是否中断过,默认没有中断过for(;;){获取node的前置节点finalNodepnode。predecessor();如果当前节点的前驱节点是头结点,并且当前节点尝试成功获取了资源if(pheadtryAcquire(arg)){就将当前节点设为头结点setHead(node);释放之前的头结点,利于垃圾回收p。helpGC表示获取资源成功返回线程是否被中断过}获取资源失败后线程等待,并检查是否被中断过if(shouldParkAfterFailedAcquire(p,node)parkAndCheckInterrupt())线程是否被中断过}}finally{if(failed)cancelAcquire(node);}}复制代码
  如果当前的前驱节点表示头结点,并且获取资源成功,那么直接将当前线程设为头结点,释放之前头结点与后继节点的链接,帮助垃圾回收(GC),如果前面当前节点的前驱不为头结点或者没有获取到资源,那么会调用shouldParkAfterFailedAcquire(Nodepred,Nodenode)方法来判断当前线程是否能够进入waiting状态,如果可以进入,并且进入到了阻塞状态,那会阻塞,直到调用了LockSupport中的unpark()方法唤醒线程。privatestaticbooleanshouldParkAfterFailedAcquire(Nodepred,Nodenode){保存前驱节点的状态提示:当waitState0时,表示该线程处于取消状态(线程中断或者等待锁超时),需要移除线程;当waitState0时,默认值,表示初始化状态,表示线程还未完成初始化操作;当waitState0,表示有效状态,线程处于可唤醒状态。intwspred。waitS等待唤醒后置节点,SIGNAL为1if(wsNode。SIGNAL)如果前置节点不是正常的等待状态(CANCELLED结束状态),那么从当前节点开始往前寻找正常的等待状态if(ws0){do{后面的节点断开与前驱节点的链接node。prevpredpred。}while(pred。waitStatus0);双向连接pred。}else{小于0时,可能为共享锁compareAndSetWaitStatus(pred,ws,Node。SIGNAL);}}复制代码
  如果前驱节点的SIGNAL值为1,会返回true。
  compareAndSetWaitStatus(pred,ws,Node。SIGNAL)方法内部也使用了CAS锁机制,源码:privatestaticfinalbooleancompareAndSetWaitStatus(Nodenode,intexpect,intupdate){returnunsafe。compareAndSwapInt(node,waitStatusOffset,expect,update);}复制代码
  如果shouldParkAfterFailedAcquire(Nodepred,Nodenode)方法返回true,则会调用parkAndCheckInterrupt()方法阻塞当前线程,线程等待,如果线程被中断过则返回true:privatefinalbooleanparkAndCheckInterrupt(){调用park让线程进入wait状态LockSupport。park(this);检查线程是否中断过。returnThread。interrupted();}复制代码
  如果线程在等待的过程中被中断过,那么获取到资源后会通知线程中断:Conveniencemethodtointerruptcurrentthread。staticvoidselfInterrupt(){Thread。currentThread()。interrupt();}复制代码
  acquire()竞争获取锁资源流程时,首先会调用tryAcquire()方法去尝试获取资源,如果成功获取到资源,则直接进入临界区执行代码;如果没有获取到资源,则将此线程封装成一个结点放入队列尾部分,调用park()方法让线程等待,并且标记为独占模式。如果线程被唤醒(unPark)时,会尝试获取锁资源,如果在等待过程中,线程被中断过则返回true,没有被中断过返回false。如果线程在等待过程中被中断过,它是不会响应,只是在获取到资源后直接调用自我中断方法selfInterrupt(),将中断线程。
  释放独占锁
  在独占锁中,释放锁的入口是release()方法,源码如下:publicfinalbooleanrelease(longarg){if(tryRelease(arg)){Nif(h!nullh。waitStatus!0)唤醒后继节点unparkSuccessor(h);}}复制代码
  点入tryRelease(arg)方法尝试释放锁,可以看出,其和tryAcquire()方法一样,也没有具体的实现,只是抛出了UnsupportedOperationException()异常,具体的逻辑由AQS的子类实现:protectedbooleantryRelease(longarg){thrownewUnsupportedOperationException();}复制代码
  如果tryRelease(longarg)方法返回true,会判断头结点是否为空,并且waitStatus是否为0(为0则代码初始化阶段),如果不为0,那么下面会调用unparkSuccessor()方法,唤醒后继节点,我们来查看unparkSuccessor()方法的源码:privatevoidunparkSuccessor(Nodenode){intwsnode。waitS如果当前状态为有效状态if(ws0)CAS操作将waitState置为0compareAndSetWaitStatus(node,ws,0);Nodesnode。下一个节点(线程)为空或已取消if(snulls。waitStatus0){从后往前遍历,寻找有效的节点for(Nt!nullt!tt。prev)if(t。waitStatus0)}if(s!null)找到则唤醒后继节点LockSupport。unpark(s。thread);}复制代码
  unparkSuccessor(Nodenode)方法实现的是唤醒后继节点,当当前节点的waitStatus状态小于0时,表示该状态为有效状态,会使用CAS机制将当前线程设为初始化状态0,之后找到下一个需要唤醒的节点,如果下一个需要唤醒的节点为空或者为取消状态则将当前线程置为null,之后从尾节点往前遍历,寻找有效的节点,找到了且不为null的话,就唤醒该节点(线程)。共享模式加锁
  在共享模式下加锁的方法入口为acquireShared(longarg)方法,其源码如下:publicfinalvoidacquireShared(longarg){if(tryAcquireShared(arg)0)doAcquireShared(arg);}复制代码
  进入到tryAcquireShared(arg)方法,此方法为尝试获取资源,得到如下源码,与独占模式获取锁一样,tryAcquireShared(longarg)没有实现具体的逻辑,由AQS的子类实现:protectedlongtryAcquireShared(longarg){thrownewUnsupportedOperationException();}复制代码
  tryAcquireShared(arg)会有三种返回值:当返回值为负数时,表示获取资源失败;当返回值为0时,表示获取资源成功,没有剩余资源;当返回值为正数时,表示当前线程获取到了资源,仍然有资源剩余。
  当tryAcquireShared(arg)方法返回为false时,表示获取资源失败,会往下进行doAcquireShared(arg)方法,此方法会将线程放入到等待队列尾部休息,点入doAcquireShared(arg)方法的源码得:privatevoiddoAcquireShared(longarg){将节点加入到队列尾部finalNodenodeaddWaiter(Node。SHARED);获取资源成功的标志,初始为获取不到try{中断的标志自旋for(;;){获取当前节点的前驱节点finalNodepnode。predecessor();如果前驱节点是头节点的话if(phead){返回还剩下多少资源longrtryAcquireShared(arg);if(r0){如果资源还足够的话,将头结点指向自己,如果还有剩余资源,可以唤醒后面的节点setHeadAndPropagate(node,r);断开之前的头结点,便于垃圾回收p。helpGCif(interrupted)selfInterrupt();}}if(shouldParkAfterFailedAcquire(p,node)parkAndCheckInterrupt())}}finally{if(failed)cancelAcquire(node);}}复制代码
  在共享模式下,当线程被唤醒拿到资源时,如果还有剩余资源,会继续唤醒后继的线程。如果被唤醒的线程发现资源不够用时会再次进入休眠。这个情况下,就算排在首位线程后面的线程需要更少的资源,也会因为前面资源不够而等待,不会先执行后面的线程。
  对于上面代码中出现的setHeadAndPropagate()方法,点入查看得到以下源码:privatevoidsetHeadAndPropagate(Nodenode,longpropagate){将头结点赋值为hNRecordoldheadforcheckbelow将当前节点设置为头结点setHead(node);如果还有剩余资源的话,会唤醒后面的节点(线程)if(propagate0hnullh。waitStatus0(hhead)nullh。waitStatus0){Nodesnode。if(snulls。isShared())唤醒后继节点doReleaseShared();}}复制代码
  释放共享资源
  释放共享资源方法为doReleaseShared()privatevoiddoReleaseShared(){自旋for(;;){N头结点不为空并且头结点不等于尾节点if(h!nullh!tail){拿到头结点的等待状态intwsh。waitS如果当前节点为等待唤醒的节点if(wsNode。SIGNAL){将当前节点等待状态初始化,来唤醒线程if(!compareAndSetWaitStatus(h,Node。SIGNAL,0))looptorecheckcasesunparkSuccessor(h);唤醒后继线程}如果线程处于初始化状态,并且还有剩余资源elseif(ws0!compareAndSetWaitStatus(h,0,Node。PROPAGATE))looponfailedCAS}如果没有后继节点,退出自旋if(hhead)}}复制代码
  在doReleaseShared()方法中,通过自旋的方式获取头节点,当头节点不为空,且队列不为空时,判断头节点的waitStatus状态的值是否为SIGNAL(1)。当满足条件时,会通过CAS将头节点的waitStatus状态值设置为0,如果CAS操作设置失败,则继续自旋。如果CAS操作设置成功,则唤醒队列中的后继节点。
  如果头节点的waitStatus状态值为0,并且在通过CAS操作将头节点的waitStatus状态设置为PROPAGATE(3)时失败,则继续自旋逻辑。
  如果在自旋的过程中发现没有后继节点了,则退出自旋逻辑。
  本篇文章就分享到这里了,后续将会分享各种其他关于并发编程的知识,感谢大佬认真读完支持咯
  文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起讨论希望能和诸佬们一起努力,今后进入到心仪的公司再次感谢各位小伙伴儿们的支持
  作者:小威要向诸佬学习呀
  链接:https:juejin。cnpost7164692725332181022
投诉 评论 转载

卡塔尔世界杯之C罗永不落幕的传奇在我心中,足球的神只有一个,那就是C罗!因为有太多的成就,辉煌,神奇瞬间被全世界球迷铭记,所以神的黄昏,也不防碍大家热爱神!这次世界杯,有13的情节是为你而来,只为一睹你……美国实在难受的不行,中国四大领域迎来突破,光刻机根本不算啥图为数字人民币近期中国取得的发展让美国实在难受的不行国在四大领域又一次迎来突破,在这些突破面前光刻机根本不算啥,这下美国又吃酸葡萄了,中国四大领域迎来重大突破,分别是数字……江苏徐州举行中秋拜月仪式再现汉时拜月盛况在徐州汉文化景区刘邦塑像广场,民众赶来拜月,同享汉文化传承。高刚摄中新网徐州9月11日电(记者朱志庚)明月几时有,把酒问青天一曲脍炙人口的《水调歌头中秋》拉开了中秋拜月仪……好吃嘴看过来,四川人卤菜必放的10种香料,你知道吗?爱旅游,爱生活。旅游可以放松自己的心情,宽阔自己的心境,你有好久没来一场说走就走的旅行,忘掉不顺心,迎接新的幸福旅程。我是元元,每天分享最新的旅游资讯。在四川人眼中,万能……贪心不足蛇吞象!女排朱婷恩师被解雇真因曝光,球队放下豪言众所周知,进入巴黎奥运周期后,不少女排球队都更换了球队的主帅,其中包括中国女排主帅郎平,日本女排的中田久美,都先后离开了球队。很大一部分原因,是因为她们在东京奥运会的带队成绩不……独占锁和共享锁底层原理分析独占锁模式前面说到,ReentrantLock锁就是基于独占锁实现的,独占锁的加锁和解锁操作都是通过互斥方式实现的。加锁流程在AQS中,是通过acquire(……为什么女性机器人这么受欢迎?买家除了不能生孩子,其他都能做智能美女机器人之所以深受欢迎,是因为除了不能生孩子之外,可以提供女性具备的任何服务。(此处已添加小程序,请到今日头条客户端查看)近几年,智能机器人在市场频繁出现,大家对智……一个转身,黎坪的秋色便悄然出现了秋季,是个十分适合出游的季节。舒适的温度,最好的旅行就应该发生在这样的季节。黎坪醉美红叶让我们一起欣赏黎坪的秋季到底有多美吧一年……立冬补冬!药补不如食补,分享5道养生食谱,滋阴进补,好吃好做头条创作挑战赛立冬是冬季的第一个节气,我国以立冬作为冬季的开始。俗语:立冬一日,水冷三分,此时正是身体补冬的好时节,常言道立冬补冬,补嘴空,不妨趁着这个时节,多多保养,为……为什么我们越来越讨厌看NBA了?全都是库里,莱昂纳德,哈登害以前的NBA,打一场比赛100比90,我们大呼精彩,现在的NBA动不动140比130,我们觉得一点儿意思也没有,那么到底有没有想过,这是为什么呢?我觉得这几点很重要!1。……脱钩中国?本田试图打造去中国化供应链之路,原因何在?近日,两条出人意料不合传统的新闻让全球汽车产业界感到了不同寻常的味道。一条是日本知名媒体《产经新闻》的报导,本田汽车试图重新调整其车型零部件供应链条,主要考虑与中国大陆地区脱钩……错失170亿美元大单后,波音再度示好,称未来半世纪专注对华合德国总理朔尔茨访华,为空客在汉堡的A320飞机生产线带回了总价值约170亿美元的订单,这也是中国航空继7月份豪掷372亿美元,向空客订购292架飞机后,又一惊人的手笔。作……
数据库的新趋势大凉山安宁河畔,52年的成昆铁路德昌站,最后的绿皮火车!遭遇感情的背叛是治愈?还是选择离开?你会如何抉择定了!天津公积金新政下周起实施!这些家庭将受益深圳男篮官宣同布克和萨林杰完成续约将代表球队征战新赛季CBA冷冻的馒头到底能不能吃?对身体有啥影响,为你揭开背后的真相吃红薯是升血糖还是降血糖?糖尿病患者能不能吃红薯?这次讲清楚原相机下的张婧仪也太好看了!网友这就是公主本人吧当年的C罗和伊莲娜配一脸血悬崖上的国美封面文章2010年八一女篮王凡病逝,2年后莫科牵线周雅菲,如今婚姻状哈登缺阵的76人射落太阳队,比分定格100比88微博人生拼搏短语说说直通车搜索人群如何优化提升股份制公司清算需要什么条件汽车点烟器短路会自燃吗点烟器短路会把其他地方烧吗《隋书赵才传》原文及翻译拔仙台上国旗忘大爷海畔歌回荡居里夫人读后感借球鞋三年级作文乡村少年宫为何有名无实孕晚期可以坐火车吗格格不入的意思格格不入的含义AlettertomyEnglish

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找七猫云易事利