Parallelität und Multithreading in Kotlin: Fortgeschrittenes Niveau

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Parallelität und Multithreading in Kotlin: Fortgeschrittenes Niveau



In der modernen Entwicklung ist effizientes Management von Parallelität der Schlüssel zur Erstellung schneller und reaktionsfähiger Anwendungen. Kotlin bietet dank der integrierten Unterstützung für Koroutinen (coroutines) ein leistungsstarkes und flexibles Toolkit, das weit über die standardmäßigen Java-Threads hinausgeht. In diesem Artikel werden wir fortgeschrittene Konzepte der Parallelität und des Multithreadings in Kotlin behandeln, einschließlich der Arbeit mit Channel, Flow, Mutex und den Feinheiten der Planer.



1. Koroutinen: Tiefer als die Grundlagen



Koroutinen sind leichtgewichtige Threads, die es ermöglichen, asynchronen Code in einem imperativen Stil zu schreiben. Auf fortgeschrittenem Niveau ist es jedoch wichtig zu verstehen, wie Dispatcher und Koroutinen-Kontexte funktionieren.



Planer (Dispatchers)

Die Wahl des richtigen Dispatchers ist entscheidend für die Leistung:

  • Dispatchers.Default — für CPU-intensive Aufgaben (standardmäßig wird ein Thread-Pool verwendet, der der Anzahl der Prozessorkerne entspricht).
  • Dispatchers.IO — für Ein-/Ausgabeoperationen (Netzwerk, Dateien). Der Thread-Pool kann auf bis zu 64 Threads erweitert werden (oder mehr, abhängig von der Konfiguration).
  • Dispatchers.Main — für den UI-Thread (Android, JavaFX).
  • newSingleThreadContext und newFixedThreadPoolContext — zum Erstellen eigener Pools.


import kotlinx.coroutines.*

fun main() = runBlocking { launch(Dispatchers.Default) { println("CPU-gebundene Aufgabe auf ${Thread.currentThread().name}") } launch(Dispatchers.IO) { println("IO-gebundene Aufgabe auf ${Thread.currentThread().name}") }}


Strukturierte Parallelität

Eine der Hauptfunktionen von Kotlin ist die strukturierte Parallelität. Sie stellt sicher, dass untergeordnete Koroutinen abgeschlossen werden, bevor die übergeordnete Koroutine beendet wird. Dies verhindert Speicherlecks und vereinfacht die Lebenszyklusverwaltung.



fun main() = runBlocking {    coroutineScope {        launch {            delay(1000)            println("Aufgabe 1 abgeschlossen")        }        launch {            delay(500)            println("Aufgabe 2 abgeschlossen")        }    }    println("Alle Aufgaben abgeschlossen")}


2. Kanäle (Channel) zur Datenübertragung zwischen Koroutinen



Channel ist das Äquivalent einer blockierenden Warteschlange (BlockingQueue), jedoch für Koroutinen. Es ermöglicht die sichere Datenübertragung zwischen Koroutinen, indem es den Sender anhält, wenn der Kanal voll ist, oder den Empfänger, wenn der Kanal leer ist.



Kanaltypen

  • RendezvousChannel (Standard) — hat keinen Puffer. Das Senden wird blockiert, bis empfangen wird.
  • BufferedChannel — hat einen Puffer mit einer bestimmten Größe.
  • ConflatedChannel — speichert nur den zuletzt gesendeten Wert und überschreibt vorherige.
  • UnlimitedChannel — unbegrenzter Puffer (Vorsicht: kann zu OOM führen).


import kotlinx.coroutines.*import kotlinx.coroutines.channels.*

fun main() = runBlocking { val channel = Channel<Int>(capacity = 5) // Gepufferter Kanal

launch { for (i in 1..10) { channel.send(i) println("Gesendet: $i") } channel.close() }

launch { for (value in channel) { println("Empfangen: $value") delay(100) } }}


Fan-out und Fan-in

Kanäle eignen sich hervorragend für die Muster "Fan-out" (ein Produzent, mehrere Konsumenten) und "Fan-in" (mehrere Produzenten, ein Konsument).



3. Flow: Reaktive Datenströme



Im Gegensatz zu Channel, der "heiß" ist (Daten werden unabhängig von der Existenz eines Abonnenten gesendet), ist Flow ein "kalter" Strom, der

Blogs

Buchempfehlungen