Ein Mutex (Kunstwort aus mutual exclusion, gegenseitiger Ausschluss) ist ein Sperrmechanismus, der sicherstellt, dass immer nur ein Thread oder Prozess gleichzeitig einen geschützten Codebereich betritt. Ohne ihn kommt es beim parallelen Zugriff auf geteilte Daten schnell zur Race Condition.
Das Eigentümer-Prinzip
Ein Mutex kennt die Zustände gesperrt und frei. Der Thread, der lock() aufruft, wird zum Eigentümer der Sperre; nur er darf unlock() ausführen. Genau diese Eigentümerbindung unterscheidet ihn vom Semaphor, bei dem jeder Thread signalisieren kann. Ein Mutex schützt genau einen Bereich; ein Zählsemaphor verwaltet dagegen Pools mit mehreren Einheiten.
Reentrant oder nicht
Ein nicht-reentranter Mutex blockiert, wenn derselbe Thread versucht, eine bereits gehaltene Sperre erneut zu erwerben — das führt sofort zu einem Selbst-Deadlock. Ein reentranter (rekursiver) Mutex erlaubt dem Eigentümer das mehrfache Sperren und zählt die Aufrufe; genauso oft muss er wieder entsperren. In Java ist synchronized auf Objekten reentrant, in C++ gibt es std::recursive_mutex. Reentrante Sperren sind bequem, können aber unsaubere Lock-Verschachtelungen verdecken.
Wie ein Mutex technisch arbeitet
Im Kern steht eine atomare Hardware-Operation wie Test-and-Set oder Compare-and-Swap. Für sehr kurze Sperren dreht der Thread in einer Schleife (Spinlock), bis die Sperre frei wird; bei längeren Wartezeiten schläft er und der Scheduler weckt ihn, sobald der Eigentümer entsperrt. Betriebssysteme mischen beide Varianten und gehen bei Überschreiten einer Dreh-Schwelle in den Schlafzustand über.
Praxis
- C++: std::mutex mit std::lock_guard oder std::unique_lock — der Guard gibt die Sperre auch bei Ausnahmen automatisch frei (RAII).
- POSIX: pthread_mutex_lock und pthread_mutex_unlock; Go: sync.Mutex; Python: threading.Lock und RLock.
- Lock-Guards und kurze Sperrzeiten sind Pflicht: Je länger der kritische Abschnitt dauert, desto mehr Threads warten.
Die größte Gefahr ist der Deadlock: Sperren zwei Threads Locks in unterschiedlicher Reihenfolge, warten sie unter Umständen ewig aufeinander. Konsistente Lock-Reihenfolge, try_lock mit Timeout und das Vermeiden verschachtelter Sperren sind die wichtigsten Gegenmaßnahmen — im Datenbankbereich tritt dasselbe Problem auf und ist dort als Deadlock ausführlich beschrieben. Für einzelne Variablen sind atomare Operationen oft die bessere Wahl als eine Sperre: Sie sind schneller und benötigen keinen Thread-Kontextwechsel.