JUC(04) ----> AQS框架设计:1.巧妙的同步队列

所属文集:一起掌握并发


1.前情概要

本篇是阅读论文《The java.util.concurrent Synchronizer Framework》 JUC同步器框架(AQS框架)原文翻译 ,并结合AQS的源码阅读之后,自己的一些思考总结。

AQS的ReentrantLock的源码阅读后,梳理出来的脑图:https://www.processon.com/view/link/5e200b65e4b0dd06803a71e9

2.自己的理解与思考

互斥:是让线程交替执行一段代码,而不能同时执行;换一个说法就是线程竞争到锁就执行同步代码,争取不到锁(同步状态)就不能执行同步代码,只能等待。

这里等待有3中方式:

  • 争取不到锁,线程休眠等待,
  • 争取不到锁,线程自旋等待,
  • 争取不到锁,线程先自旋等待一会儿,不行再休眠等待(AQS的做法)。

锁:AQS中,是否加锁状态,使用int类型状态变量来记录,通过CAS操作来修改其值;0表示无锁,1表示加锁,>1表示重入,重入时执行++操作,记录重入次数;释放锁时执行--操作,直到减到0才表示锁被完全释放,可被其他线程抢占。
休眠:休眠和唤醒使用unsafe中的park(休眠线程)和unpark(唤醒线程)方法。
自旋:在java语言中的for循环,循环次数是有限的。
等待:等待使用双向队列的结构来实现排队等待的效果,不能插队的模式是公平模式,可插队模式是非公平模式。

2.1 单向队列的需求和设计:

引用原文:

在原始版本的CLH锁中,节点间甚至都没有互相链接。自旋锁中,pred变量可以是一个局部变量。然而,Scott和Scherer证明了通过在节点中显式地维护前驱节点,CLH锁就可以处理“超时”和各种形式的“取消”:如果一个节点的前驱节点取消了,这个节点就可以滑动去使用前面一个节点的状态字段。

立即原文:可以比较容易的处理超时和取消类型的请求,在队列的组织结构下,前一个请求因为取消或者超时而变得报废无效了,后边的请求往前挤压,可以把无效的移除队列,以便让处于存活有效状态的向前移动。

为什么选择tail向head方向(pre链路)?
从tail到head,即pre方向更自然,队末的更有需求让队伍往前走,因为自己的事还没办;队首的自己的事已经办完了,后边排队的跟自己关系不大。
比如堵车的时候,总是后车的打喇叭催促前车快走。如果前车故障停下不走(类比线程超时、取消),后车因为要往前走,就让前车靠边,自己绕过继续前行(类比tail向前滑动,将取消、中断的移出队列)

2.2 自旋修改为自旋+阻塞

1.原文说

第二个对CLH队列主要的修改是将每个节点都有的状态字段用于控制阻塞而非自旋”,... “取消”状态必须存在于状态字段中"。

把面向自旋的设计修改为了面向阻塞
先看自旋的设计,非常容易理解:

每一个节点的“释放”状态都保存在其前驱节点中。因此,自旋锁的“自旋”操作就如下:
while (pred.status != RELEASED); // spin
自旋后的出队操作只需将head字段指向刚刚得到锁的节点:
head = node;

但是全部是自旋也浪费CPU,修改成阻塞试试:

while (pred.status  != RELEASED){
  阻塞休眠
}

所有线程一上来,看状态不满足,就阻塞也不合适;比如第一个排队的,跟其他后续排队的情况不同,对于第一个排队的,其前边的可能已经或者很快就结束,这种情况下最好先自旋几此尝试去竞争锁,如果失败了再阻塞,而其他后续的排队的节点就可以上来就排队阻塞。

原文中”检查当前节点的前驱是否为head来确定权限"也是这个意思,即不用判断前驱节点的release状态,只需要判断是不是head,是head就尝试竞争锁,不是head就阻塞(park)排队,等着被唤醒。

2.3 双向队列的需求和设计:

线程拿不到锁会排队阻塞,后续就需要被唤醒;如何唤醒后继的节点,前边提到 队列被设计为tail 到 head方向的链路;从tail顺着pre往head方向找总能找到,但是优化一下,能直接找到自己的后继节点就会更方便。所以增加next方向,队列就变成了双向。但是双向的链路控制不是原子的,保证pre方向及时有效,next方向略有延迟,所以,在next方向找不到的情况下,要尝试从tail沿着pre方向往前找一下。

2.4 后继节点如何阻塞,如何唤醒

当前节点release后,后继节点可能正在自旋竞争锁呢,这种情况下没有必要去唤醒它(虽然unpark唤醒操作也不会出错,但也是有成本的);出于成基考虑再优化一下,在休眠之前,最好给前驱节点个“唤醒(signal me)”自己的信号。
所以线程调用park前,给前驱节点设置一个“唤醒(signal me)”标志,并再尝试一次去拿锁,如果竞争到了锁,就避免了一次不必要的阻塞;如果竞争不到锁,才去真的休眠。

2.5 队列延迟初始化

只有一个线程的时候,或者线程是交替执行,且线程之间无交叉,这种情况下,线程都不需要排队,只需要判断同步状态,修改同步状态;因此队列是在首次需要的时候才进行初始化(构建一个虚拟节点,head和tail都指向它)。

2.6 排队策略:公平与非公平

非公平:插队模式,往队首插队,插不进去,才追加到队尾。
公平:不可插队模式,到队尾排队。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355

推荐阅读更多精彩内容