Zum Inhalt springen
Lektion 24 von 26 18 Min. Profi Zur Übung Mit Codebeispiel

Nebenläufigkeit

Nebenläufigkeit bedeutet: Mehrere Aufgaben laufen scheinbar gleichzeitig. Das ist mächtig, aber auch eine der größten Fehlerquellen in Java.

Ansicht

01

Verstehen

Lies die Kernidee und das Beispiel.

02

Prüfen

Beantworte die Aktiv-Checks.

03

Anwenden

Setze die Transferaufgabe um.

Mini-Erfolg

Du hast zwei Aufgaben parallel gestartet und den Unterschied zur sequenziellen Ausführung gesehen.

Warum das wichtig ist: Nebenläufigkeit ist mächtig, aber fehleranfällig. Schon ein kleines Verständnis hilft, seltsame Bugs zu erkennen.

Das darf schwer sein

Das ist Profi-Stoff. Niemand erwartet, dass du das beim ersten Durchlauf beherrschst. Verstehen und Ausprobieren reicht.

Lerninhalt

Verstehen & anwenden

1

Threads starten

Ein Thread ist ein Ausführungsstrang.

Mit start() beginnt er zu laufen; run() direkt aufzurufen würde keinen neuen Thread starten.

Die Reihenfolge der Ausgaben ist nicht garantiert. Genau das macht Nebenläufigkeit spannend und schwierig.

ThreadRunnablestart()
Zwei Threads
Thread a = new Thread(() -> System.out.println("A"));
Thread b = new Thread(() -> System.out.println("B"));

a.start();
b.start();

// Die Reihenfolge von A und B kann variieren.
2

Race Conditions

Wenn zwei Threads dieselbe Variable ohne Schutz verändern, kann eine Änderung verloren gehen.

Das nennt man Race Condition.

synchronized sorgt dafür, dass immer nur ein Thread den geschützten Block betritt. Es löst nicht jedes Problem, macht aber den kritischen Abschnitt sicher.

Race ConditionsynchronizedShared State
Gemeinsamer Zähler
class Zaehler {
    private int stand = 0;

    public synchronized void erhoehen() {
        stand++;
    }

    public synchronized int getStand() {
        return stand;
    }
}
3

ExecutorService

Für viele Aufgaben ist es besser, einen Thread-Pool zu nutzen, statt für jede Aufgabe einen neuen Thread zu starten.

ExecutorService nimmt Aufgaben an und liefert Future-Objekte für das Ergebnis. Am Ende rufst du shutdown() auf.

ExecutorServiceFutureshutdown()
Thread-Pool
ExecutorService pool = Executors.newFixedThreadPool(2);
Future<Integer> ergebnis = pool.submit(() -> 21 * 2);

System.out.println(ergebnis.get()); // 42
pool.shutdown();

Vertiefung

Nebenläufigkeit sichtbar machen

Zwei Threads können dieselbe Variable gleichzeitig lesen und zurückschreiben. Ohne Schutz geht eine Änderung verloren — das ist eine Race Condition. synchronized macht den kritischen Abschnitt atomar.

Für viele Aufgaben ist ein Thread-Pool besser als viele einzelne Threads. ExecutorService verwaltet die Threads, Future liefert das Ergebnis. Am Ende immer shutdown() aufrufen.

Sicherer Zähler
class Zaehler {
    private int stand;
    synchronized void plus() { stand++; }
    synchronized int get() { return stand; }
}

ExecutorService pool = Executors.newFixedThreadPool(4);
for (int i = 0; i < 1000; i++) pool.submit(zaehler::plus);
pool.shutdown();

Merksatz: Gemeinsame Daten brauchen Schutz. Ein Thread-Pool begrenzt die Parallelität und macht Ressourcen beherrschbar.

Übungsaufgabe

Sichere Zählung

Starte mehrere Threads, die denselben Zähler erhöhen. Vergleiche eine unsichere und eine synchronized-Version.

Das soll dein Programm können

  • Zählerklasse mit plus()
  • Mehrere Threads oder ExecutorService
  • Synchronized-Variante
  • Ergebnis ausgeben und erklären

Schreibe den Code hier oder in deiner IDE und vergleiche ihn dann mit der Lösung.

Dein Code wird automatisch auf diesem Gerät gespeichert.

Hinweis anzeigen

Mit synchronized void plus() wird die Methode geschützt. Starte 1000 Aufgaben über einen ExecutorService und warte mit shutdown() und awaitTermination.

Lösung anzeigen
Sichere Zählung.java
import java.util.concurrent.*;

public class Zaehlen {
    static class Zaehler {
        private int stand = 0;
        synchronized void plus() { stand++; }
        synchronized int get() { return stand; }
    }

    public static void main(String[] args) throws Exception {
        Zaehler zaehler = new Zaehler();
        ExecutorService pool = Executors.newFixedThreadPool(4);

        for (int i = 0; i < 1000; i++) {
            pool.submit(zaehler::plus);
        }

        pool.shutdown();
        pool.awaitTermination(5, TimeUnit.SECONDS);
        System.out.println("Stand: " + zaehler.get()); // 1000
    }
}

Warum funktioniert das?

Ohne synchronized könnten zwei Threads denselben alten Wert lesen und beide 1 addieren — am Ende fehlt eine Erhöhung. Mit synchronized ist die Methode atomar.

Aktiv-Check

Teste dein Verständnis

1/6

Erkennen: Welcher Abschnitt passt?

Welcher Abschnitt passt zu diesen Begriffen?

ThreadRunnablestart()
Tipp: Erst erinnern, dann prüfen. Fehler sind Teil des Lernens.

Spaßprojekt

Zwei Aufgaben, ein Wettlauf

Starte zwei Threads, die unterschiedlich lange schlafen, und beobachte die Reihenfolge der Ausgaben. Wiederhole es mehrmals.

Selbstcheck

Kann ich das schon?

Hake ab, was du wirklich erklären oder selbst bauen könntest.

0 von 3 geschafft

Fortschritt

Bereit zum Abschließen?

Markiere die Lektion als abgeschlossen, um XP und Streak zu erhalten.

</> Learn-Program © 2026 · Lizenziert unter CC BY-NC-SA 4.0

Rechtliches & Lizenz