前言

asongGoGojavaGo

什么是可重入锁

java

假设现在有多个村民在水井排队打水,有管理员正在看管这口水井,村民在打水时,管理员允许锁和同一个人的多个水桶绑定,这个人用多个水桶打水时,第一个水桶和锁绑定并打完水之后,第二个水桶也可以直接和锁绑定并开始打水,所有的水桶都打完水之后打水人才会将锁还给管理员。这个人的所有打水流程都能够成功执行,后续等待的人也能够打到水。这就是可重入锁。

下图摘自美团技术团队分享的文章:

如果是非可重入锁,,此时管理员只允许锁和同一个人的一个水桶绑定。第一个水桶和锁绑定打完水之后并不会释放锁,导致第二个水桶不能和锁绑定也无法打水。当前线程出现死锁,整个等待队列中的所有线程都无法被唤醒。

下图依旧摘自美团技术团队分享的文章:

Go
javaReentrantLock
ReentrantLockAQSAQSstatusstatus0statusstatus == 0status1status != 0status+1statusstatus-1 == 0

总结一下实现一个可重入锁需要这两点:

  • 记住持有锁的线程
  • 统计重入的次数

统计重入的次数很容易实现,接下来我们考虑一下怎么实现记住持有锁的线程?

GoGoroutineGoGoGoroutineGoroutineGoroutineGoroutinepanicGo语言Goroutine IDruntime.StackGoroutine ID
goGoroutineGoroutine ID
Mutexhostgoroutine idrecursiongoroutinesync.CondCondGoroutinecondsync.Cond
  • 构造函数
Lock

这里逻辑比较简单,大概解释一下:

GoroutineIDGoroutineresutsionGoroutineGoroutineGoroutinecond.wait
Unlock

大概解释如下:

GoroutinepanicGoroutinecond.Signal
github
Go
Go

举个例子,假设我们现在一段这样的代码:

F()G()

这样不仅避免了死锁,而且还对代码进行了解耦。这样的代码按照作用范围进行了分层,就像金字塔一样,上层调用下层的函数,越往上作用范围越大;各层有自己的锁。

Go

总结

Go
asong