Parallelität und Multithreading in Kotlin: von A bis Z

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: von A bis Z



Moderne Anwendungen erfordern zunehmend die gleichzeitige Ausführung mehrerer Aufgaben. In Kotlin, wie auch in anderen JVM-Sprachen, wird Multithreading traditionell über die Klasse Thread realisiert. Die Sprache bietet jedoch ein weitaus leistungsfähigeres und eleganteres Werkzeug – Koroutinen (coroutines). In diesem Artikel werden wir alle Aspekte der Parallelität und des Multithreadings in Kotlin behandeln: von den grundlegenden Konzepten bis hin zu fortgeschrittenen Techniken.



1. Grundlagen des Multithreadings: Thread und Runnable



Beginnen wir mit dem klassischen Ansatz. In Kotlin, wie auch in Java, kann man einen Thread direkt erstellen:



fun main() {    val thread = Thread {        for (i in 1..5) {            println("Thread: $i")            Thread.sleep(500)        }    }    thread.start()    println("Hauptthread beendet")}


Die manuelle Thread-Verwaltung hat jedoch Nachteile:

  • Hoher Overhead – Erstellen und Zerstören von Threads ist teuer.
  • Komplexität der Synchronisation – leicht kann ein Race Condition auftreten.
  • Begrenzte Anzahl von Threads – das System kann nur eine begrenzte Anzahl von Threads erstellen.


Für die Synchronisation des Zugriffs auf gemeinsame Daten wird das Schlüsselwort synchronized oder Sperren ReentrantLock verwendet:



class Counter {    private var count = 0    @Synchronized    fun increment() {        count++    }}


2. Koroutinen – der moderne Ansatz zur Parallelität



Koroutinen sind leichtgewichtige Threads, die die Probleme des klassischen Multithreadings lösen. Sie sind nicht an System-Threads gebunden und können auf einem einzigen Thread ausgeführt werden, indem sie zwischen Aufgaben umschalten.



2.1. Erste Koroutine



Um mit Koroutinen zu arbeiten, muss die Abhängigkeit org.jetbrains.kotlinx:kotlinx-coroutines-core eingebunden werden. Beispiel einer einfachen Koroutine:



import kotlinx.coroutines.*

fun main() = runBlocking { launch { delay(1000L) println("Welt!") } println("Hallo,")}


Hier ist runBlocking die Brücke zwischen normalem Code und Koroutinen. launch startet eine neue Koroutine, und delay ist das Analogon zu Thread.sleep(), blockiert jedoch den Thread nicht.



2.2. Suspend-Funktionen



Das Schlüsselwort suspend ermöglicht es, die Ausführung einer Funktion anzuhalten, ohne den Thread zu blockieren:



suspend fun fetchData(): String {    delay(2000L) // Simulation einer Netzwerkanfrage    return "Daten empfangen"}

fun main() = runBlocking { val result = async { fetchData() } println("Warten...") println(result.await())}


2.3. Strukturierte Nebenläufigkeit



Kotlin unterstützt strukturierte Nebenläufigkeit – alle untergeordneten Koroutinen werden automatisch abgeschlossen, bevor die übergeordnete Koroutine beendet wird:



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


3. Fortgeschrittene Techniken: Channel, Flow und Dispatchers



3.1. Channels



Channels ermöglichen den Datenaustausch zwischen Koroutinen. Sie sind das Analogon zu einer Warteschlange:



fun main() = runBlocking {    val channel = Channel<Int>()        launch {        for (i in 1..5) {            channel.send(i * i)        }        channel.close()    }        for (value in channel) {        println(value)    }}


3.2. Flow – kalte Datenströme



Flow ist ein reaktiver Strom, der Werte auf Anfrage emittiert:



fun simpleFlow(): Flow<Int> = flow {    for (i in 1..3) {        delay(                

Blogs

Buchempfehlungen