Go语言Mutex加锁流程

       go语言sync包的mutex设计还是挺巧妙的,也看了网上其他介绍的文章,加锁的这个流程细节还是挺复杂,特别是里面用到了三个比特位来表示锁状态,我觉得主要明白其中心思想就好了。这里主要是利用goland编辑器的plantuml来画了一个时序图,熟悉一下plantuml的使用,感觉效果还行。

       团队中多人开发,也可以用plantuml,如果其他人想修改流程,通过编辑plantuml代码可以很方便的修改图。而且画出来的效果看着还不错。

中心思想:
1.乐观锁,进来就尝试CAS修改锁状态进行抢锁
2.进入慢锁竞争等待流程
2.1 正常模式:唤醒等待队列时,会唤醒所有线程,谁先抢到锁就是谁的
2.2 饥饿模式:饥饿模式下排队,由释放锁的线程直接唤醒队列头部线程拿锁先到达的先获取锁,后到的进入队列尾部。

@startuml
'https://plantuml.com/sequence-diagram

'自动编号'
autonumber

client -> mutex: 请求加锁
activate mutex #FFBBBB
alt 通过CAS判断当前锁是否空闲\n并操作当前锁的状态
mutex -[#FF0000]-> client: 空闲则尝试加锁,返回成功
note right: 结束 注释

else 已加锁,进入慢加锁流程
loop 循环抢锁
alt 锁忙碌 AND 非饥饿模式 AND 系统可以自旋

alt 当前线程非唤醒状态 AND \n互斥锁没有已经苏醒的线程 AND \n有其他线程等待锁
mutex --> mutex: 如修改互斥锁状态为有唤醒者成功\n就把当前线程标记为唤醒
end

mutex --> mutex: 进入自旋 自旋记录+1 \n返回loop
end

alt 当前互斥锁不是饥饿模式
mutex-->mutex: 标记临时变量为加锁
end

alt 当前锁是忙碌或饥饿模式
mutex --> mutex: 标记临时变量队列等待数+1
end

alt 如果是当前线程已是饥饿状态 AND 当前锁忙碌
mutex-->mutex: 标记临时变量为饥饿模式
end

alt 如果当前线程是唤醒线程
mutex-->mutex: 标记临时变量为无唤醒线程
end

alt 原子操作,将临时变量保存到锁变量,如成功
alt 如果锁以前是空闲 AND 非饥饿
mutex --> client:抢锁成功
note right
这里是正常模式下
各个线程竞争抢锁,成功就返回
end note
end

mutex --> mutex: 阻塞等待信号量
mutex --> mutex: 其他释放锁,该线程被唤醒
mutex --> mutex: 检查自己的等待时间,判断是否进入饥饿状态

alt 当前锁是饥饿模式
mutex --> mutex: 等待线程数减1\n计算锁的饥饿状态
mutex --> mutex: 修改锁状态
mutex --> client: 抢锁成功
note right
这里是饥饿模式下
由释放锁的线程直接唤醒队列头部线程拿锁
其他等待线程继续等待
end note

end note
end
mutex --> mutex: 标记自己为苏醒状态

end

end
deactivate mutex

note left
中心思想
1.乐观锁,进来就尝试CAS修改锁状态进行抢锁
2.进入慢锁竞争等待流程
2.1 正常模式:唤醒等待队列时,会唤醒所有线程,谁先抢到锁就是谁的
2.2 饥饿模式:饥饿模式下排队,由释放锁的线程直接唤醒队列头部线程拿锁
                      先到达的先获取锁,后到的进入队列尾部
end note
@enduml

参考:

Go精妙的互斥锁设计

开源工具,使用简单的文字描述画UML图。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值