DEV Community

Cover image for Concurrency Programming (3): Mutexes — Atomicity, Visibility, and Ordering at the Language Level
ThinkerQAQ
ThinkerQAQ

Posted on Originally published at thinkerqaq.github.io

Concurrency Programming (3): Mutexes — Atomicity, Visibility, and Ordering at the Language Level

Table of Contents


0. Continue from the Previous Article

This article discusses Mutexes directly from the rules defined at the language memory model layer.


1. What Guarantees Do the Lock Rules in Each Language Provide?

1.1 Java: synchronized and Monitors

Java's synchronized locks the monitor associated with an object. JLS §17.1 states:

“Only one thread at a time may hold a lock on a monitor.”

This rule provides the exclusion needed for atomicity. At most one thread can hold the monitor, so only that thread may execute the protected critical section while competitors wait.

JLS §17.4.5 also states:

“An unlock on a monitor happens-before every subsequent lock on that monitor.”

This rule provides both visibility and ordering.

For visibility, A's writes are before its unlock, while B's reads are after the subsequent lock. Through happens-before transitivity, the writes in A's critical section become visible to B.

For ordering, A's program order, the unlock → lock relationship, and B's program order form one happens-before chain. B therefore cannot observe a result such as ready = true, counter = 0 when that would violate the established ordering.

1.2 Go: sync.Mutex

Go's sync.Mutex is not owned by a particular goroutine and is not reentrant. sync.Mutex.Lock states:

“If the lock is already in use, the calling goroutine blocks until the mutex is available.”

This gives the exclusion needed for atomicity. While one goroutine holds the mutex, other goroutines attempting to acquire the same mutex wait rather than entering the critical section.

The Go Memory Model - Locks further states:

“For any sync.Mutex or sync.RWMutex variable l and n < m, call n of l.Unlock() is synchronized before call m of l.Lock() returns.”

This rule provides visibility and ordering.

For visibility, A's writes are sequenced-before Unlock; Unlock is synchronized-before B's Lock returns; B's reads are sequenced after that return. Together these relationships form happens-before, so A's writes are visible to B.

For ordering, the same happens-before chain connects A's writes, Unlock → Lock, and B's reads. B likewise cannot observe ready = true, counter = 0 when the synchronization relationship requires the earlier counter write to be visible.

1.3 CPython: threading.Lock

Python's official threading.Lock documentation states:

“A primitive lock is a synchronization primitive that is not owned by a particular thread when locked.”

“All methods are executed atomically.”

The locking semantics provide mutual exclusion: when the lock is already acquired, another thread's acquire() waits until the lock becomes available, so the same lock serializes entry into the protected critical section.

Visibility and ordering cannot be derived from a formal language-level memory-model rule in the same way as in Java or Go. Python does not define a general release → acquire happens-before relationship comparable to those specifications; the documentation defines Lock as a synchronization primitive.

Python programs should therefore use the same Lock to synchronize shared state rather than depending on the GIL or on whether a particular bytecode sequence happens to be interruptible in a particular implementation. How CPython implements the cross-critical-section synchronization boundary is an implementation question for the next article.


2. Next: How Is a Mutex Implemented?

This article stops at the language level: it looks at what guarantees a Mutex provides to programmers from the perspectives of Atomicity, Visibility, and Ordering.

The next article turns those language-level guarantees into implementation-level problems, following Java synchronized, Go sync.Mutex, and CPython threading.Lock from the Runtime down to the CPU.


This article was first published on ThinkerQAQ's personal blog and syndicated here by the author. The original article may be revised over time; please refer to the personal blog for the latest version.

Top comments (0)