目录
本文首发于 ThinkerQAQ 的个人博客,由作者本人同步发布。
0. 从上一篇继续
这一篇直接从语言内存模型层的规则讨论 Mutex。
1. 三种语言的锁规则分别保证什么?
1.1 Java:synchronized 与 Monitor
Java 的 synchronized 锁定对象关联的 Monitor。JLS §17.1 规定:
"Only one thread at a time may hold a lock on a monitor."
这条规则对应 Atomicity。同一时刻只有一个线程能持有 Monitor,所以只有它能进入临界区,其他使用同一 Monitor 的线程只能等待。
JLS §17.4.5 还规定:
"An unlock on a monitor happens-before every subsequent lock on that monitor."
这条规则同时对应 Visibility 和 Ordering。
对于 Visibility,A 的写入发生在 unlock 之前,B 的读取发生在后续 lock 之后。通过 happens-before 的传递性,A 在临界区中的写入对 B 可见。
对于 Ordering,A 内部的写入顺序、unlock → lock 和 B 内部的读取顺序被连成一条 happens-before 链。因此,B 不能看到 ready = true、counter = 0 这种违反该顺序的结果。
1.2 Go:sync.Mutex
Go 的 sync.Mutex 不绑定某个 Goroutine,也不提供可重入语义。sync.Mutex.Lock 规定:
"If the lock is already in use, the calling goroutine blocks until the mutex is available."
这条规则对应 Atomicity。一个 Goroutine 持有 Mutex 时,其他 Goroutine 会等待,不能进入受同一 Mutex 保护的临界区。
The Go Memory Model - Locks 还规定:
"For any
sync.Mutexorsync.RWMutexvariableland n < m, call n ofl.Unlock()is synchronized before call m ofl.Lock()returns."
这条规则同时对应 Visibility 和 Ordering。
对于 Visibility,A 的写入 sequenced-before Unlock,Unlock 又 synchronized-before B 的 Lock 返回,B 的读取发生在 Lock 返回之后。这些关系组成 happens-before,因此 A 的写入对 B 可见。
对于 Ordering,同一条 happens-before 链把 A 内部的写入顺序、Unlock → Lock 和 B 内部的读取顺序连起来。因此,B 同样不能看到 ready = true、counter = 0。
1.3 CPython:threading.Lock
Python 官方的 threading.Lock 文档规定:
"A primitive lock is a synchronization primitive that is not owned by a particular thread when locked."
"All methods are executed atomically."
第二条规则对应 Atomicity。Lock 已经被获得时,其他线程的 acquire() 会等待;因此同一时刻只有一个线程能进入受同一把 Lock 保护的临界区。
Visibility 和 Ordering 则不能像 Java、Go 那样从一条正式的 Memory Model 规则推出。Python 没有定义 release → acquire 的 happens-before 关系;官方文档只把 Lock 定义为 synchronization primitive。
因此,Python 程序应该使用同一把 Lock 同步共享状态,而不是依赖 GIL 或某条字节码当前是否可中断。在 CPython 中,Lock 如何落实跨临界区的可见性和顺序边界,需要到下一篇继续下钻实现。
2. 下一篇:互斥锁是怎么实现的?
这一篇停在语言层:从 Atomicity、Visibility 和 Ordering 三个方面,看一把 Mutex 向程序员提供什么保证。
下一篇会把这些语言层保证转换成实现层真正需要解决的问题,分别沿着 Java synchronized、Go sync.Mutex 和 CPython threading.Lock,从 Runtime 一直看到 CPU。