Viele typische Operationen einer Anwendung dauern lange, ohne dass die CPU dabei etwas tut: eine Antwort vom Server abwarten, eine Datei von der Festplatte lesen, auf eine Datenbankabfrage warten. Der naive Ansatz — jede Operation der Reihe nach abwarten — ist einfach zu schreiben, verschwendet aber Zeit: Die Wartezeiten addieren sich, obwohl nichts davon parallel laufen müsste.
Ein klassischer Ausweg sind zusätzliche Threads: Jede langsame Operation läuft auf ihrem eigenen Thread, während der Hauptthread weiterarbeitet. Das funktioniert, hat aber einen Preis. Threads sind vom Betriebssystem verwaltete Ressourcen — jeder braucht Speicher, und ihre Anzahl ist begrenzt. Threads sind außerdem teuer im Wechsel (Context Switch) und schwer zu debuggen: Race Conditions und Deadlocks entstehen schnell, wenn mehrere Threads auf denselben Zustand zugreifen.
Zwei ältere Ansätze versuchen, ohne zusätzliche Threads auszukommen:
thenApply, thenCompose und Ähnliches).
Das vermeidet tiefe Verschachtelung, verlangt aber ein anderes Programmiermodell als gewohnter,
sequenzieller Code, und jede Bibliothek bringt ihre eigene API mit.Kotlins Antwort sind Coroutines: asynchroner Code, der aussieht und sich liest wie normaler, sequenzieller Code, aber Wartezeiten nicht blockierend, sondern durch Suspendieren überbrückt. Eine Coroutine kann an einer Wartestelle pausieren, den Thread für andere Arbeit freigeben und später genau dort weitermachen. Wie das im Detail funktioniert, ist Thema der nächsten Lektionen — diese Lektion hält nur fest, welches Problem damit gelöst wird.
import kotlin.system.measureTimeMillis
fun ladeProfil(): String {
Thread.sleep(500)
return "Profil geladen"
}
fun ladeEinstellungen(): String {
Thread.sleep(500)
return "Einstellungen geladen"
}
fun main() {
val dauer = measureTimeMillis {
println(ladeProfil())
println(ladeEinstellungen())
}
println("Gesamtdauer: $dauer ms")
}
Beide Funktionen blockieren den Thread mit Thread.sleep, nur um eine langsame Operation zu
simulieren. Nacheinander aufgerufen, addieren sich die Wartezeiten — genau das Problem, das die
folgenden Lektionen mit Coroutines lösen.
Lies den Code genau durch, bevor du ihn ausführst.
import kotlin.system.measureTimeMillis
fun ladeProfil(): String {
Thread.sleep(500)
return "Profil geladen"
}
fun ladeEinstellungen(): String {
Thread.sleep(500)
return "Einstellungen geladen"
}
fun main() {
val dauer = measureTimeMillis {
println(ladeProfil())
println(ladeEinstellungen())
}
println("Gesamtdauer: $dauer ms")
}
ladeProfil() und ladeEinstellungen() blockieren je 500ms. Wie lange dauert der komplette Block ungefähr, und warum?
Ein Bestellsystem prüft beim Checkout drei Dinge: das Nutzerprofil, den Warenkorb und den Lagerbestand. Jede Prüfung braucht Zeit (simuliert mit Thread.sleep). Ergänze die fehlende Funktion pruefeLagerbestand (analog zu den beiden anderen, mit Thread.sleep(800) und Rückgabewert true) und rufe im main-Block alle drei Prüfungen nacheinander auf. Miss die Gesamtdauer mit measureTimeMillis und gib sie aus.
Bevor du einen Hint bekommst: Was hast du schon probiert, und woran hängt es genau?
1. Warum ist ein zusätzlicher Thread pro langsamer Operation keine unbegrenzt skalierbare Lösung?
2. Was ist ein zentrales Problem von callback-basiertem asynchronem Code?