sync.locker是无实现的接口契约,必须传非nil指针(如*sync.mutex),字段声明为sync.locker易因未初始化导致nil panic,函数参数需满足指针接收者实现,复杂锁需求应避免抽象。

sync.Locker 接口本身不能实例化,它只是个契约——你传进去的必须是具体锁的指针,比如 *sync.Mutex 或 *sync.RWMutex,否则编译直接报错。
为什么传 sync.Locker 却 panic: "nil pointer dereference"
常见原因是结构体字段声明了 mu sync.Locker,但没初始化就调用 Lock():
-
sync.Locker是接口类型,零值是nil,和*sync.Mutex的零值(未锁定)完全不同 - 写
mu sync.Locker看似抽象,实则埋雷;正确做法是声明为mu *sync.Mutex或注入时确保非 nil - 如果真要用接口字段,必须显式赋值,例如
c.mu = &sync.Mutex{},不能依赖零值
sync.Locker 作为函数参数时,传什么才合法
只能传实现了该接口的指针类型,值类型或接口变量本身都不行:
- ✅ 合法:
lock(&sync.Mutex{})、lock(myRWMutexPtr)(*sync.RWMutex实现了Lock()/Unlock()) - ❌ 非法:
lock(sync.Mutex{})(值类型不满足接口,方法集不匹配) - ❌ 非法:
var l sync.Locker; lock(l)(l是 nil,运行时报 panic) - 注意:
sync.RWMutex虽然也实现sync.Locker,但一旦这么用,就丢掉了RLock()和RUnlock()能力
mock 测试时实现 sync.Locker 容易漏掉的关键点
自定义 mock 锁不是写两个空方法就行,接收者类型和指针语义必须对齐:
- 必须用指针接收者:
func (m *mockLocker) Lock(),不能是func (m mockLocker) Lock() - 值接收者实现的接口,无法用
&mockLocker{}赋值给sync.Locker变量(方法集不等价) - 测试中若想验证是否加锁,需在
Lock()里改状态,并在断言前检查,例如require.True(t, mock.locked) - 别忘了
Unlock()也要做对应状态清理,否则多次调用会干扰后续断言
什么时候不该用 sync.Locker 抽象
它只覆盖最基础的阻塞式互斥,稍复杂需求就失效:
- 需要超时获取锁?
sync.Locker没有TryLock()或带context.Context的方法,得换golang.org/x/sync/semaphore或自定义 - 读多写少场景想用读锁优化?
sync.Locker强制退化为互斥语义,sync.RWMutex的读并发能力被屏蔽 - 要集成分布式锁(如 etcd lease)?那些实现通常不实现
sync.Locker,因为语义不匹配(网络延迟、租约续期等)
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











