/
/
·
·
/
/
·
·
  • Einführung
  • Die Idee in einem Satz
  • Einen Thread starten
  • Das Kernproblem: Race Condition
  • Die Lösung: synchronized
  • volatile und Deadlock (kurz)
  • Klausur-Faustregeln
  • Häufige Stolpersteine
ThemenProgrammiergrundlagenThreads und Nebenläufigkeit
Programmiergrundlagen·4Lerneinheiten·27min·Stand18.07.2026

Threads und Nebenläufigkeit.

Ein Programm macht normalerweise eine Sache nach der anderen. Mit Threads laufen mehrere Abläufe scheinbar gleichzeitig: ein Download im Hintergrund, während die UI reagiert; mehrere Anfragen parallel bedient. Das ist mächtig, aber sobald sich zwei Threads denselben Speicher teilen, entstehen Fehler, die es im Single-Thread-Code nicht gibt: Race Conditions. Genau die sind der Prüfungsschwerpunkt in Programmieren 2.

Was du können musst:

  • einen Thread starten (Runnable, start() vs. run(), join())
  • erklären, warum counter++ bei mehreren Threads kaputtgeht (Race Condition)
  • eine Race Condition mit synchronized beheben (gegenseitiger Ausschluss)
  • volatile von synchronized abgrenzen (Sichtbarkeit vs. Atomarität)
  • ein Deadlock-Szenario erkennen

Abgrenzung: hier geht es um die Grundlagen (Thread, synchronized, Race Condition, Deadlock). Tiefe Nebenläufigkeit (java.util.concurrent, Fork/Join, das formale Speichermodell) ist Master-Stoff.

Ein Thread ist ein eigenständiger Ausführungsfaden innerhalb eines Programms. Mehrere Threads teilen sich denselben Speicher, deshalb muss der Zugriff auf gemeinsamen veränderlichen Zustand koordiniert werden, sonst entstehen Race Conditions.

// Runnable (funktionales Interface) ist der bevorzugte Weg
Runnable aufgabe = () -> System.out.println("Laeuft in " + Thread.currentThread().getName());

Thread t = new Thread(aufgabe);
t.start();      // startet einen NEUEN Thread, der run() ausfuehrt
t.join();       // wartet, bis t fertig ist

start() vs. run() ist die häufigste Anfänger-Falle: t.start() erzeugt einen neuen Thread. t.run() ruft die Methode nur direkt im aktuellen Thread auf, also gar kein zweiter Thread. join() lässt den aufrufenden Thread warten, bis der andere fertig ist.

counter++ sieht aus wie eine Operation, ist aber in Wahrheit drei: den Wert lesen, plus eins rechnen, zurückschreiben (read-modify-write). Wenn sich zwei Threads dabei verschränken, lesen beide denselben alten Wert, und ein Update geht verloren.

class Zaehler {
    int counter = 0;
    void inc() { counter++; }   // NICHT atomar!
}
// Zwei Threads rufen je 1x inc() auf. Erwartet: 2. Tatsaechlich: manchmal 1.

Im Stepper siehst du genau, wie ein Update verloren geht.

Lade Visualisierung...

synchronized sorgt für gegenseitigen Ausschluss (mutual exclusion): immer nur ein Thread ist im geschützten Block, die anderen warten. Java nutzt dafür einen intrinsischen Lock (das Objekt selbst bzw. this).

class Zaehler {
    private int counter = 0;
    synchronized void inc() { counter++; }   // jetzt atomar pro Aufruf
    // oder: void inc() { synchronized (this) { counter++; } }
}

Damit wird der read-modify-write unteilbar: Thread B kommt erst rein, wenn A komplett fertig ist, und liest dann A's aktuelles Ergebnis.

Lade Visualisierung...

volatile garantiert Sichtbarkeit: schreibt ein Thread ein volatile-Feld, sehen andere Threads den neuen Wert sofort (kein veralteter Cache-Wert). Aber volatile macht NICHT atomar, für counter++ reicht es nicht (das bleibt read-modify-write). Für atomares Zählen: synchronized oder AtomicInteger.

Deadlock: Zwei Threads halten je einen Lock und warten auf den jeweils anderen, beide blockieren für immer.

// Thread 1: synchronized(A) { synchronized(B) { ... } }
// Thread 2: synchronized(B) { synchronized(A) { ... } }   // umgekehrte Reihenfolge!

Lösung: Locks immer in derselben Reihenfolge anfordern.

1. counter++ ist nicht atomar. Es ist read-modify-write (3 Schritte). Gemeinsamer veränderlicher Zustand plus mehrere Threads ergibt eine Race Condition.

2. synchronized = gegenseitiger Ausschluss. Nur ein Thread im Block oder in der Methode, über einen intrinsischen Lock (this bzw. das angegebene Objekt).

3. start() startet einen Thread, run() nicht. t.start() erzeugt einen neuen Thread (der run() ausführt). t.run() ist nur ein normaler Methodenaufruf im selben Thread.

4. join() wartet auf das Ende. Ohne join() läuft der aufrufende Thread (oft main) womöglich weiter, bevor die anderen fertig sind.

5. volatile sichert Sichtbarkeit, nicht Atomarität. Für sichtbaren, aber zusammengesetzten Zugriff (counter++) braucht es synchronized oder AtomicInteger.

6. Deadlock durch gegenläufige Lock-Reihenfolge. Zwei Threads, zwei Locks, umgekehrt angefordert, beide warten ewig. Vorbeugung: konsistente Lock-Reihenfolge.

1. t.run() statt t.start(). Startet keinen neuen Thread, alles läuft im aufrufenden Thread. Klassische Klausur-Falle.

2. Race Condition als "passiert schon nicht" abtun. Sie ist nicht-deterministisch: läuft tausendmal gut, dann unter Last plötzlich falsch. Schwer reproduzierbar, deshalb gefährlich.

3. volatile als Lock missverstehen. Es schützt nicht vor Race Conditions bei counter++, nur vor veralteten Werten. Atomarität braucht synchronized/Atomic*.

4. Über-Synchronisierung. Alles in einen großen synchronized-Block packen serialisiert komplett, dann hätte man auch single-threaded bleiben können. Nur den kritischen Abschnitt schützen.

5. Reihenfolge der Threads annehmen. Es gibt keine Garantie, welcher Thread wann läuft, das entscheidet der Scheduler. Code darf nicht von einer bestimmten Reihenfolge abhängen.

6. Vergessen, dass main auch ein Thread ist. Ohne join() kann main enden oder weiterlaufen, bevor die gestarteten Threads ihr Ergebnis geschrieben haben.

Derselbe Thread, zwei Aufrufe. Einer startet echte Nebenläufigkeit, der andere nicht. Achte im Stepper auf den Namen des ausführenden Threads in der Ausgabe, das verrät, ob ein neuer Thread entstanden ist.

Lade Visualisierung...

Klausur-Tipp: Wenn eine Aufgabe fragt "wie viele Threads laufen?" oder "in welchem Thread läuft das?", suche nach start() (neuer Thread) versus run() (selber Thread). Und: ohne join() ist die Ausgabe-Reihenfolge mehrerer Threads nicht vorhersagbar.

Anmelden, um den Fortschritt zu speichern.

Nächster Schritt

Wenn du fertig bist: jetzt üben.

Aktives Abrufen festigt Wissen schneller als nochmal lesen.

War das hilfreich?

Verwandte Themen

  • Variablen und Datentypen
  • Bedingungen
  • Schleifen
  • Funktionen
  • Rekursion

Tools

Bald: Karteikarten · Spaced-Repetition · Mind-Map-Export

Fachliche Qualität
S-Tier · GoldstandardZuletzt geprüft am 12.06.2026

Diese Lerneinheit wurde für typische Bachelor-Klausuren konzipiert. So prüfen wir · Fehler entdeckt? Melde ihn uns oder markiere die fragliche Stelle direkt im Text oben.

Klausur-ÜbersichtKomplette Übersicht: alle Tabs als linearer Text zum Lernen
▾

Alle Tabs der Lerneinheit (Erklärung · Interaktiv verstehen · Praxis-Übung · Klausur-Quiz) als durchgehender Text. Ideal zum Wiederholen vor der Klausur, und für Suchmaschinen wie Google, Bing und KI-Suche (ChatGPT, Perplexity).

Inhalt dieser Übersicht

  1. Erklärung(Erklärung)
  2. Interaktiv verstehen(Visualisierung / Interaktiv)
  3. Praxis-Übung(Quiz / Klausurfragen)
  4. Klausur-Quiz(Quiz / Klausurfragen)
Teil 1·Erklärung

Erklärung

Ein Programm macht normalerweise eine Sache nach der anderen. Mit Threads laufen mehrere Abläufe scheinbar gleichzeitig: ein Download im Hintergrund, während die UI reagiert; mehrere Anfragen parallel bedient. Das ist mächtig, aber sobald sich zwei Threads denselben Speicher teilen, entstehen Fehler, die es im Single-Thread-Code nicht gibt: Race Conditions. Genau die sind der Prüfungsschwerpunkt in Programmieren 2.

Was du können musst:

  • einen Thread starten (Runnable, start() vs. run(), join())
  • erklären, warum counter++ bei mehreren Threads kaputtgeht (Race Condition)
  • eine Race Condition mit synchronized beheben (gegenseitiger Ausschluss)
  • volatile von synchronized abgrenzen (Sichtbarkeit vs. Atomarität)
  • ein Deadlock-Szenario erkennen

Abgrenzung: hier geht es um die Grundlagen (Thread, synchronized, Race Condition, Deadlock). Tiefe Nebenläufigkeit (java.util.concurrent, Fork/Join, das formale Speichermodell) ist Master-Stoff.

Die Idee in einem Satz

Ein Thread ist ein eigenständiger Ausführungsfaden innerhalb eines Programms. Mehrere Threads teilen sich denselben Speicher, deshalb muss der Zugriff auf gemeinsamen veränderlichen Zustand koordiniert werden, sonst entstehen Race Conditions.

Einen Thread starten

// Runnable (funktionales Interface) ist der bevorzugte Weg
Runnable aufgabe = () -> System.out.println("Laeuft in " + Thread.currentThread().getName());

Thread t = new Thread(aufgabe);
t.start();      // startet einen NEUEN Thread, der run() ausfuehrt
t.join();       // wartet, bis t fertig ist

start() vs. run() ist die häufigste Anfänger-Falle: t.start() erzeugt einen neuen Thread. t.run() ruft die Methode nur direkt im aktuellen Thread auf, also gar kein zweiter Thread. join() lässt den aufrufenden Thread warten, bis der andere fertig ist.

Das Kernproblem: Race Condition

counter++ sieht aus wie eine Operation, ist aber in Wahrheit drei: den Wert lesen, plus eins rechnen, zurückschreiben (read-modify-write). Wenn sich zwei Threads dabei verschränken, lesen beide denselben alten Wert, und ein Update geht verloren.

class Zaehler {
    int counter = 0;
    void inc() { counter++; }   // NICHT atomar!
}
// Zwei Threads rufen je 1x inc() auf. Erwartet: 2. Tatsaechlich: manchmal 1.

Im Stepper siehst du genau, wie ein Update verloren geht.

Interaktive Visualisierung

Interaktive Komponente: probiere sie im Topic-Player oben aus.

Die Lösung: synchronized

synchronized sorgt für gegenseitigen Ausschluss (mutual exclusion): immer nur ein Thread ist im geschützten Block, die anderen warten. Java nutzt dafür einen intrinsischen Lock (das Objekt selbst bzw. this).

class Zaehler {
    private int counter = 0;
    synchronized void inc() { counter++; }   // jetzt atomar pro Aufruf
    // oder: void inc() { synchronized (this) { counter++; } }
}

Damit wird der read-modify-write unteilbar: Thread B kommt erst rein, wenn A komplett fertig ist, und liest dann A's aktuelles Ergebnis.

Interaktive Visualisierung

Interaktive Komponente: probiere sie im Topic-Player oben aus.

volatile und Deadlock (kurz)

volatile garantiert Sichtbarkeit: schreibt ein Thread ein volatile-Feld, sehen andere Threads den neuen Wert sofort (kein veralteter Cache-Wert). Aber volatile macht NICHT atomar, für counter++ reicht es nicht (das bleibt read-modify-write). Für atomares Zählen: synchronized oder AtomicInteger.

Deadlock: Zwei Threads halten je einen Lock und warten auf den jeweils anderen, beide blockieren für immer.

// Thread 1: synchronized(A) { synchronized(B) { ... } }
// Thread 2: synchronized(B) { synchronized(A) { ... } }   // umgekehrte Reihenfolge!

Lösung: Locks immer in derselben Reihenfolge anfordern.

Klausur-Faustregeln

1. counter++ ist nicht atomar. Es ist read-modify-write (3 Schritte). Gemeinsamer veränderlicher Zustand plus mehrere Threads ergibt eine Race Condition.

2. synchronized = gegenseitiger Ausschluss. Nur ein Thread im Block oder in der Methode, über einen intrinsischen Lock (this bzw. das angegebene Objekt).

3. start() startet einen Thread, run() nicht. t.start() erzeugt einen neuen Thread (der run() ausführt). t.run() ist nur ein normaler Methodenaufruf im selben Thread.

4. join() wartet auf das Ende. Ohne join() läuft der aufrufende Thread (oft main) womöglich weiter, bevor die anderen fertig sind.

5. volatile sichert Sichtbarkeit, nicht Atomarität. Für sichtbaren, aber zusammengesetzten Zugriff (counter++) braucht es synchronized oder AtomicInteger.

6. Deadlock durch gegenläufige Lock-Reihenfolge. Zwei Threads, zwei Locks, umgekehrt angefordert, beide warten ewig. Vorbeugung: konsistente Lock-Reihenfolge.

Häufige Stolpersteine

1. t.run() statt t.start(). Startet keinen neuen Thread, alles läuft im aufrufenden Thread. Klassische Klausur-Falle.

2. Race Condition als "passiert schon nicht" abtun. Sie ist nicht-deterministisch: läuft tausendmal gut, dann unter Last plötzlich falsch. Schwer reproduzierbar, deshalb gefährlich.

3. volatile als Lock missverstehen. Es schützt nicht vor Race Conditions bei counter++, nur vor veralteten Werten. Atomarität braucht synchronized/Atomic*.

4. Über-Synchronisierung. Alles in einen großen synchronized-Block packen serialisiert komplett, dann hätte man auch single-threaded bleiben können. Nur den kritischen Abschnitt schützen.

5. Reihenfolge der Threads annehmen. Es gibt keine Garantie, welcher Thread wann läuft, das entscheidet der Scheduler. Code darf nicht von einer bestimmten Reihenfolge abhängen.

6. Vergessen, dass main auch ein Thread ist. Ohne join() kann main enden oder weiterlaufen, bevor die gestarteten Threads ihr Ergebnis geschrieben haben.

Teil 2·Visualisierung / Interaktiv

Interaktiv verstehen

start() oder run()? Die Klausur-Falle

Derselbe Thread, zwei Aufrufe. Einer startet echte Nebenläufigkeit, der andere nicht. Achte im Stepper auf den Namen des ausführenden Threads in der Ausgabe, das verrät, ob ein neuer Thread entstanden ist.

Interaktive Visualisierung

Interaktive Komponente: probiere sie im Topic-Player oben aus.

Klausur-Tipp: Wenn eine Aufgabe fragt "wie viele Threads laufen?" oder "in welchem Thread läuft das?", suche nach start() (neuer Thread) versus run() (selber Thread). Und: ohne join() ist die Ausgabe-Reihenfolge mehrerer Threads nicht vorhersagbar.

Teil 3·Quiz / Klausurfragen

Praxis-Übung

Klausurfragen mit Lösungen (6)

F1.Was startet einen neuen Thread, der die Aufgabe nebenläufig ausführt?

Antwort: t.start()

Erklärung: t.start() erzeugt einen neuen Thread und ruft dort run() auf. t.run() ruft die Methode nur direkt im aktuellen Thread auf (kein neuer Thread). execute() und go() gibt es bei Thread nicht.

F2.counter++ ist eine atomare Operation, die nicht von anderen Threads unterbrochen werden kann.

Antwort: Falsch

Erklärung: Falsch. counter++ ist read-modify-write (lesen, plus eins, schreiben), also drei Schritte. Zwei Threads koennen sich dazwischen verschraenken und ein Update verlieren. Das ist die klassische Race Condition.

Typ: Wahr/Falsch

F3.Zwei Threads rufen je 1000-mal eine ungeschützte inc()-Methode (counter++) auf. Welcher Endwert ist möglich?

Antwort: Irgendetwas zwischen 1000 und 2000, nicht-deterministisch

Erklärung: Ohne Synchronisierung gehen durch Race Conditions Updates verloren. Der Endwert liegt irgendwo zwischen 1000 (sehr viele verloren) und 2000 (Idealfall), und ist von Lauf zu Lauf verschieden. Genau diese Nicht-Determiniertheit macht den Bug gefaehrlich.

F4.Was bewirkt das Schlüsselwort synchronized an einer Methode?

Antwort: Nur ein Thread kann die Methode (auf demselben Objekt) gleichzeitig ausführen

Erklärung: synchronized erzwingt gegenseitigen Ausschluss ueber den intrinsischen Lock des Objekts: nur ein Thread ist gleichzeitig drin, die anderen warten. Es macht NICHT parallel (im Gegenteil, es serialisiert) und nicht schneller.

F5.Ordne jedes Werkzeug seiner Wirkung zu.

Zuordnungen:

  • synchronized → gegenseitiger Ausschluss (Atomarität)
  • volatile → Sichtbarkeit über Threads hinweg
  • join() → warten, bis ein Thread fertig ist
  • start() → neuen Thread erzeugen

Erklärung: synchronized = Atomarität/Ausschluss, volatile = Sichtbarkeit (nicht Atomarität), join = auf Thread-Ende warten, start = neuen Thread erzeugen.

Typ: Zuordnung

F6.Warum reicht volatile NICHT, um counter++ über mehrere Threads korrekt zu machen?

Antwort: volatile sichert nur die Sichtbarkeit, aber counter++ bleibt read-modify-write und damit nicht atomar

Erklärung: volatile garantiert, dass Threads den aktuellen Wert sehen (keine veralteten Caches), aber es macht den zusammengesetzten read-modify-write von counter++ nicht unteilbar. Zwei Threads koennen weiterhin denselben Wert lesen. Loesung: synchronized oder AtomicInteger.

Teil 4·Quiz / Klausurfragen

Klausur-Quiz

Klausurfragen mit Lösungen (6)

F1.Was gibt dieser Code aus?
Thread t = new Thread(() ->
    System.out.println(Thread.currentThread().getName()));
t.run();

Antwort: main

Erklärung: t.run() ruft run() direkt im aktuellen Thread auf (main), es entsteht kein neuer Thread. Ausgabe: main. Erst t.start() wuerde einen neuen Thread (Thread-0) erzeugen.

F2.Was ist eine Race Condition?

Antwort: Wenn das Ergebnis von der zeitlichen Verschränkung mehrerer Threads abhängt und dadurch falsch werden kann

Erklärung: Eine Race Condition liegt vor, wenn das Ergebnis von der (nicht kontrollierbaren) zeitlichen Verschraenkung der Threads abhaengt, typisch beim ungeschuetzten Zugriff auf gemeinsamen veraenderlichen Zustand (z.B. counter++).

F3.synchronized macht Code schneller, weil mehrere Threads gleichzeitig hineindürfen.

Antwort: Falsch

Erklärung: Falsch. synchronized laesst nur EINEN Thread gleichzeitig in den Block, die anderen warten. Es serialisiert also und macht an dieser Stelle eher langsamer. Der Zweck ist Korrektheit (kein Lost Update), nicht Geschwindigkeit.

Typ: Wahr/Falsch

F4.counter++ ist nicht atomar, sondern ein {{1}}-modify-write. Gegen Race Conditions hilft {{2}} (gegenseitiger Ausschluss). Damit ein Thread Änderungen eines anderen sofort sieht, dient {{3}}, das aber keine Atomarität garantiert.

Lösungen pro Lücke:

  • {{1}}: read / lesen
  • {{2}}: synchronized
  • {{3}}: volatile

Erklärung: read-modify-write, synchronized (Ausschluss/Atomarität), volatile (Sichtbarkeit, nicht Atomarität). Das Standard-Vokabular der Nebenläufigkeits-Grundlagen.

Typ: Lückentext

F5.Wozu dient t.join() nach t.start()?

Antwort: Es lässt den aufrufenden Thread warten, bis t fertig ist

Erklärung: join() blockiert den aufrufenden Thread, bis t durchgelaufen ist. Ohne join() koennte main weiterlaufen oder enden, bevor t sein Ergebnis geschrieben hat.

F6.Thread 1 macht synchronized(a){ synchronized(b){...} }, Thread 2 macht synchronized(b){ synchronized(a){...} }. Was kann passieren?

Antwort: Deadlock: jeder hält einen Lock und wartet auf den anderen

Erklärung: Klassischer Deadlock: Thread 1 haelt a und will b, Thread 2 haelt b und will a. Beide warten ewig. Vorbeugung: Locks immer in derselben Reihenfolge anfordern (erst a, dann b, in beiden Threads).

Zur KategorieProgrammiergrundlagen.Mehr Themen entdeckenZum Themen-Hub.

UniProMax ist eine themenbasierte Lernplattform für Studierende an deutschen Unis.

Wir glauben, dass Verstehen besser ist als Auswendiglernen. Wir bauen Lerneinheiten die zeigen statt erzählen. Code, Visualisierung, Quiz. Auf Deutsch.

Marke

UniProMaxUniProMax

Themenbasiert, visuell, interaktiv.

Inhalte

  • Alle Themen (Hub)
  • Programmiergrundlagen
  • Algorithmen
  • Mathematik
  • Statistik
  • Datenbanken
  • Rechnungswesen
  • VWL

Studiengang-Filter

  • Informatik
  • Wirtschaftsinformatik
  • BWL
  • Data Science
  • VWL
  • Wirtschaftsingenieurwesen
  • Mathe
  • Psychologie
  • weitere Studiengänge folgen

Plattform

  • Mein Fortschritt
  • Impressum
  • Datenschutz
© 2026 UniProMaxAlle Systeme onlinev0.2 / Sommersemester 2026
UniProMaxUniProMaxUniProMaxUniProMax