首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >trylock 用法_Java lock

trylock 用法_Java lock

作者头像
全栈程序员站长
发布2022-11-07 17:15:40
发布2022-11-07 17:15:40
8440
举报

在并发编程中,为了避免多线程同时读写共享资源,我们需要互斥。Go 标准库提供了互斥锁 sync.Mutex ,通过加锁 Lock() 方法和解锁 Unlock() 方法达到对共享资源的并发控制。

在之前的设计中,当锁被占有,其他 goroutine 尝试获取锁时会被阻塞。这种方式当然是合理的,但是在某些情况下,或许我们希望在获取锁失败时,并不想停止执行,而是可以进入其他的逻辑。

在 Go 1.18 中,为 sync.Mutex 新增了一个新的方法 TryLock(),它是一种非阻塞模式的取锁操作。当调用 TryLock() 时,该函数仅简单地返回 true 或者 false,代表是否加锁成功。

有了 TryLock 的存在,我们就可以由这样的代码

代码语言:javascript
复制
m.Lock()
 // 阻塞等待加锁成功后的逻辑

转变成这样的逻辑

代码语言:javascript
复制
if m.TryLock(){
 // 加锁成功的逻辑
 }else {
 // 加锁失败的逻辑
 }

TryLock 实现

Go精妙的互斥锁设计一文中,我们详细分析过互斥锁的设计,其代码轻量简洁,通过巧妙的位运算,仅仅采用 state 一个字段就实现了四个字段的效果,非常之精彩,建议感兴趣的读者一读。

而 TryLock() 的实现更加简单

代码语言:javascript
复制
func (m *Mutex) TryLock() bool {
 old := m.state
 if old&(mutexLocked|mutexStarving) != 0 {
  return false
 }

 // There may be a goroutine waiting for the mutex, but we are
 // running now and can try to grab the mutex before that
 // goroutine wakes up.
 if !atomic.CompareAndSwapInt32(&m.state, old, old|mutexLocked) {
  return false
 }

 if race.Enabled {
  race.Acquire(unsafe.Pointer(m))
 }
 return true
}

当锁被其他 goroutine 占有,或者当前锁正处于饥饿模式,它将立即返回 false。

代码语言:javascript
复制
func (m *Mutex) Lock() {
 // Fast path: grab unlocked mutex.
 if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
  if race.Enabled {
   race.Acquire(unsafe.Pointer(m))
  }
  return
 }
 // Slow path (outlined so that the fast path can be inlined)
 m.lockSlow()
}

而当锁可用时,TryLock() 会采用与 Lock() 方法一样的方式去尝试获取锁。但在获取失败时,与 Lock() 将不一样,它不会自旋或者阻塞。这是一个完全的非阻塞获取方式。

应用场景

正如 TryLock() 方法的注释一样,它的应用场景并不常见,并且也不被鼓励使用。

代码语言:javascript
复制
// Note that while correct uses of TryLock do exist, they are rare,
// and use of TryLock is often a sign of a deeper problem
// in a particular use of mutexes.

在当前 Go1.18 标准库源码中,与 Lock() 方法被大量内部使用而截然不同的是,并没有找到一处使用 TryLock() 的地方,仅仅在测试文件 mutex_test.go 中,有找到该方法的新增测试用例。

这里贴一个 TryLock 的使用场景讨论:https://stackoverflow.com/questions/41788074/use-case-for-lock-trylock

另外,在开源社区已经有不少 Go 的 TryLock 实现库。它们基于 sync.Mutex 通过 CAS 操作和 unsafe 指针实现 ;或者利用 channel 实现。

但是这些库都不能竞态检测。因此,官方支持实现 TryLock 是必要的,避免 TryLock 被滥用。且由于可以集成竞态检测,相较于三方库实现,有利于开发者发现问题。

总结

从 2012 年开始,实际上很早就有关于 Go 增加 TryLock 的 issue 讨论,但是直到 Go 1.18 才被增加。这其中很大一部分原因是,并没有合理的案例值得添加 TryLock。

Go Team 的负责人 rsc 之前提出的反对意见:TryLock 会鼓励开发者对锁进行不精确的思考,并最终导致竞态问题。

另外,Go 1.18 除了为互斥锁 sync.Mutex 新增了 TryLoc() 方法外,也为读写锁 sync.RWMutex 新增了相应的 TryRLock() 和 TryLock() 方法。

正如新增的这三个方法的注释,虽然使用它们的情况存在,但很少见,使用需谨慎。

感谢你的点赞在看哦~

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/183685.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年10月10日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • TryLock 实现
  • 应用场景
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档