atomic 适合简单数值增减、标志位切换、指针替换等无锁场景,开销纳秒级、多核扩展性好;需内存对齐,atomic.value 仅支持整体替换,不支持结构体等复合类型字段级操作。

Atomic 适合什么场景?
只做简单数值增减、标志位切换、指针替换时,atomic 是首选。它不涉及 OS 调度,开销在纳秒级,多核扩展性好。
- 典型操作包括:
atomic.AddInt64、atomic.LoadUint32、atomic.StorePointer、atomic.CompareAndSwapInt64 - 必须保证变量内存对齐(
int64在 64 位系统上需 8 字节对齐),否则某些平台(如 32 位 ARM)可能 panic -
atomic.Value可安全存取任意类型,但仅限“整体替换”,不能修改其内部字段;且每次Store都会分配新对象,高频写入要注意 GC 压力 - 不支持结构体、map、slice 等复合类型——试图对 struct 字段单独原子操作是无效的,Go 不保证字段级原子性
Mutex 锁住的是代码段,不是变量
很多人误以为 sync.Mutex 是“给某个变量加锁”,其实它锁的是临界区逻辑。同一个 mu 实例可以保护多个字段、甚至跨函数调用的整段状态变更。
- 必须成对使用
Lock()和Unlock();推荐写法是mu.Lock(); defer mu.Unlock(),避免 return 分支遗漏解锁 - 禁止复制已使用的
sync.Mutex:Go 1.19+ 会在运行时 panic;若结构体含sync.Mutex,应嵌入*sync.Mutex或确保始终传指针 - 不可重入:同一个 goroutine 连续两次
Lock()会死锁,没有“可重入锁”概念 - 锁粒度要合理:锁整个 map 的读写比锁单个 key 更重;高频小操作用
atomic或sync.Map更合适
RWMutex 不是 Mutex 的升级版
sync.RWMutex 只在“读远多于写”(比如读写比 > 4:1)时才比普通 Mutex 有优势。它内部维护读计数器和写等待队列,逻辑更重。
- 写请求具有高优先级:一旦有 goroutine 调用
Lock(),后续所有RLock()都会被阻塞,直到写完成——这可能导致读饥饿 - 读锁无法升级为写锁:在
RLock()后直接调用Lock()是死锁定式,必须先RUnlock()再Lock() -
RLock()和RUnlock()必须严格配对;未RLock()就RUnlock()会 panic - 不要为了“看起来更高级”而滥用 RWMutex:对单个 int64 计数器加
RWMutex,性能反而比Mutex差
计数器选型:看有没有分支逻辑
是否用 atomic,关键不在“是不是计数器”,而在“操作是否纯原子、无条件分支”。
- 纯递增/递减:用
atomic.AddInt64(&c.count, 1)—— 简单、快、无锁 - 带条件判断(如“只在小于阈值时加一”):必须用
Mutex,因为if + atomic.Load + atomic.Add组合不是原子的,中间可能被其他 goroutine 插入 - 需要 CAS 循环重试(如“加一但不超过 100”):可用
atomic.CompareAndSwapInt64手动实现,但逻辑复杂易错,多数情况不如Mutex直观可靠 - 注意
atomic.LoadInt64返回的是快照值,不能用于“检查后执行”的业务逻辑,除非配合 CAS 或锁
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











