Thread-Sicherheit (engl. Thread Safety) bezeichnet die Eigenschaft von Code oder Datenstrukturen, auch dann korrekt zu arbeiten, wenn gleichzeitig mehrere Threads darauf zugreifen. Ein Thread ist ein eigenständig laufender Ausführungspfad innerhalb eines Prozesses. Ein thread-sicheres Objekt liefert unter Nebenläufigkeit immer konsistente Ergebnisse – ohne dass sich die Aufrufer selbst um Synchronisation kümmern müssen.
Das Kernproblem: Data Races
Ein Data Race entsteht, wenn mindestens zwei Threads auf dieselbe Speicherstelle zugreifen und mindestens einer davon schreibt – ohne gemeinsame Synchronisation. Die Folge ist meist ein undefiniertes Verhalten: Werte können überschrieben, gelesen oder teilweise aktualisiert werden. Genau eine solche Race Condition macht Thread-Sicherheit unmöglich, wenn der Zustand nicht geschützt wird.
Strategien für thread-sicheren Code
- Sperren (Locks): Der kritische Abschnitt wird durch einen Mutex oder eine Lese-Schreib-Sperre geschützt. Bewährt in Java (
synchronized), C++ (std::mutex), Python (threading.Lock) und Go (sync.Mutex). - Atomare Operationen: Für einfache Zähler oder Flags sind atomare Operationen schneller als Sperren – sie nutzen Hardware-Instruktionen wie Compare-and-Swap und sind besonders in C++ (
std::atomic) und Go (sync/atomic) üblich. - Unveränderlichkeit (Immutability): Objekte, die nach der Erstellung nie verändert werden, sind von Natur aus thread-sicher – ein weiterer Grund, warum funktionale Stile und unveränderliche Datenstrukturen in parallelen Systemen beliebt sind.
- Thread-lokaler Speicher: Daten, die jeder Thread ausschließlich für sich hält (
ThreadLocal,thread_local), erzeugen gar keine Konflikte.
Abstufungen und Kosten
Thread-Sicherheit ist nicht immer binär: Manche Container sind nur für gleichzeitige Lesevorgänge sicher, andere für alle Zugriffe. Sperren kosten Performance (Contention) und können bei falscher Schachtelung zu einem Deadlock oder Livelock führen. Deshalb kombinieren moderne Bibliotheken mehrere Techniken: Nebenläufige Kollektionen (z. B. ConcurrentHashMap in Java) schichten Atomics, Teil-Sperren und Copy-on-Write übereinander.
Ein klassisches Beispiel für eine grundlegende Synchronisationslösung ist der Peterson-Algorithmus, der mit zwei Flaggen und einer turn-Variable wechselseitigen Ausschluss für zwei Prozesse ermöglicht – die konzeptionelle Basis, auf der moderne Synchronisationsprimitive wie Semaphor, Monitor-Synchronisation und Bedingungsvariable aufbauen.
Verwandte Grundlagen: Producer-Consumer, Futex, Spinlock.