Verwenden Sie `synced()` und `withLock()`, um gemeinsam genutzte Ressourcen in Kotlin zu sperren

David Mbochi Njonge 20 Juni 2023
  1. Generieren Sie ein Kotlin-Projekt
  2. Thread-Interferenz in Aktion
  3. Sperren Sie gemeinsam genutzte Ressourcen mit der Funktion synchronized() in Kotlin
  4. Sperren Sie gemeinsam genutzte Ressourcen mit der Funktion withLock() in Kotlin
  5. Abschluss
Verwenden Sie `synced()` und `withLock()`, um gemeinsam genutzte Ressourcen in Kotlin zu sperren

Bei der Arbeit mit Multithread-Anwendungen müssen wir sicherstellen, dass der Zugriff auf gemeinsam genutzte Ressourcen verwaltet wird, um inkonsistente Daten zu vermeiden. Synchronisierung ist der Ansatz, den wir normalerweise verwenden, wenn wir den Zugriff auf gemeinsam genutzte Ressourcen in unseren Anwendungen steuern möchten.

Die Synchronisierung wird mithilfe von Sperren realisiert, wodurch sichergestellt wird, dass kein Thread auf die gemeinsam genutzte Ressource zugreift, bis der aktuell ausgeführte Thread seine Ausführung beendet hat. Ein Thread muss zuerst die Sperre eines Objekts erhalten, bevor er auf die gemeinsam genutzte Ressource zugreift, und sie nach Abschluss freigeben.

Es gibt verschiedene Möglichkeiten, Sperren zu erstellen, und in diesem Tutorial lernen wir, wie Sperren in Kotlin erstellt werden, um den Zugriff auf freigegebene Ressourcen zu kontrollieren.

Generieren Sie ein Kotlin-Projekt

Öffnen Sie die IntelliJ-Entwicklungsumgebung und wählen Sie Datei > Neu > Projekt. Geben Sie im sich öffnenden Fenster den Projektnamen als kotlin-withlock ein, wählen Sie Kotlin im Abschnitt Sprache und wählen Sie Intellij als Build System. Drücken Sie abschließend die Schaltfläche Erstellen, um das Projekt zu generieren.

Nachdem die Anwendung generiert wurde, erstellen Sie eine Datei mit dem Namen Main.kt im Ordner src/main/kotlin. Wir werden diese Datei verwenden, um alle Codebeispiele für dieses Tutorial zu implementieren.

Thread-Interferenz in Aktion

Thread-Interferenzen treten in einer Multithread-Anwendung ohne Kontrolle darüber auf, wie auf gemeinsam genutzte Ressourcen zugegriffen wird. Der folgende Code zeigt, wie die Thread-Interferenz in einer Multithread-Anwendung auftritt.

Kopieren Sie den folgenden Code und fügen Sie ihn in die Datei Main.kt ein.

class Calculator {
    fun addIntArray() {
        var initialValue = 0;
        for (number in 1..5) {
            println(
                Thread.currentThread().name
                        + " ---> " + initialValue + " + "
                        + number + " = "
                    .plus(String.format("%d", (initialValue + number)))
            );
            initialValue += number;

            Thread.sleep(500);
        }
    }
}

fun main() {
    val calculator = Calculator();

    Thread {
        calculator.addIntArray();
    }.start();

    Thread {
        calculator.addIntArray();
    }.start();
}

In diesem Code haben wir eine Methode namens addIntArray() innerhalb einer Klasse namens Calculator erstellt. Diese Methode ist eine gemeinsam genutzte Ressource in unserem Code, und da wir mehrere Threads erstellen werden, haben wir eine println()-Methode hinzugefügt, die es uns ermöglicht, den aktuell ausgeführten Thread in der Konsole zu protokollieren.

Wenn die sleep()-Methode aufgerufen wird, wird der aktuell ausgeführte Thread für 500 Millisekunden angehalten und ein anderer Thread beginnt zu laufen. Das bedeutet, dass die Threads gleichzeitig auf unsere Methode zugreifen, was zu Dateninkonsistenzen führen kann.

In der Methode main() haben wir zwei Threads erstellt und die Methode start() für sie aufgerufen, um mit der Ausführung der Methode addIntArray() des von uns erstellten Rechnerobjekts zu beginnen.

Führen Sie diesen Code aus und beachten Sie, wie die Threads verschachtelt sind, wie unten gezeigt.

Thread-0 ---> 0 + 1 = 1
Thread-1 ---> 0 + 1 = 1
Thread-0 ---> 1 + 2 = 3
Thread-1 ---> 1 + 2 = 3
Thread-1 ---> 3 + 3 = 6
Thread-0 ---> 3 + 3 = 6
Thread-1 ---> 6 + 4 = 10
Thread-0 ---> 6 + 4 = 10
Thread-0 ---> 10 + 5 = 15
Thread-1 ---> 10 + 5 = 15

Sperren Sie gemeinsam genutzte Ressourcen mit der Funktion synchronized() in Kotlin

Dieses Beispiel verwendet die Methode addIntArray(), die wir im vorherigen Abschnitt implementiert haben, um zu zeigen, wie der Zugriff darauf verwaltet wird. Kommentieren Sie die im vorherigen Beispiel erstellte Methode main() aus und kopieren Sie den folgenden Code und fügen Sie ihn nach dem Kommentar in die Datei Main.kt ein.

val calculator = Calculator();

fun sumUsingSynchronization(): Unit = synchronized(calculator) {
    calculator.addIntArray();
}

fun main() {

    Thread {
       sumUsingSynchronization();
    }.start();

    Thread {
        sumUsingSynchronization()
    }.start();
}

Das Hauptziel dieses Lernprogramms besteht darin, zu lernen, wie Sperren erstellt werden, um den exklusiven Zugriff von Threads auf eine gemeinsam genutzte Ressource sicherzustellen. Standardmäßig haben Objekte implizite Monitorsperren, die Threads verwenden, um eine Synchronisation zu erreichen, und dieser Code verwendet eine implizite Monitorsperre, indem er das Calculator-Objekt an die synchronized()-Funktion übergibt.

Die Funktion synchronized() wird von der Funktion sumUsingSynchronization() aufgerufen, die wiederum die Methode addIntArray() aufruft. In der Methode main() haben wir zwei Threads erstellt und die Methode start() für sie aufgerufen, um die Ausführung zu starten.

Beachten Sie, dass es keine Interferenz zwischen den beiden Threads gibt, da unser Code eine implizite Monitorsperre verwendet. Der erste Thread verwendet ausschließlich die Methode addIntArray(), bis er seine Ausführung beendet hat, bevor der zweite Thread startet.

Die Funktion synchronized() ist nicht nur auf die impliziten Monitorsperren beschränkt, sondern kann auch beliebige andere Sperren wie die ReentrantLock verwenden, und wir müssen nur die Sperre als Argument dieser Methode übergeben. Führen Sie diesen Code aus und stellen Sie sicher, dass die Ausgabe wie unten gezeigt ist.

Thread-0 ---> 0 + 1 = 1
Thread-0 ---> 1 + 2 = 3
Thread-0 ---> 3 + 3 = 6
Thread-0 ---> 6 + 4 = 10
Thread-0 ---> 10 + 5 = 15
Thread-1 ---> 0 + 1 = 1
Thread-1 ---> 1 + 2 = 3
Thread-1 ---> 3 + 3 = 6
Thread-1 ---> 6 + 4 = 10
Thread-1 ---> 10 + 5 = 15

Sperren Sie gemeinsam genutzte Ressourcen mit der Funktion withLock() in Kotlin

Kommentieren Sie die im vorherigen Beispiel erstellte Methode main() aus und kopieren Sie den folgenden Code und fügen Sie ihn nach dem Kommentar in die Datei Main.kt ein.

import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock

val reentrantLock: Lock = ReentrantLock();

fun sumUsingLock(): Unit = reentrantLock.withLock {
    calculator.addIntArray();
}

fun main() {

    Thread {
       sumUsingLock();
    }.start();

    Thread {
        sumUsingLock()
    }.start();
}

In diesem Code haben wir eine Methode namens sumUsingLock() erstellt, die der Erweiterungsfunktion withLock() der Schnittstelle Lock zugewiesen ist. Die Erweiterungsfunktion withLock() verwendet die bereitgestellte Lock-Implementierung, um den Codeblock exklusiv für jeden der mehreren erstellten Threads auszuführen.

In diesem Beispiel haben wir mit ReentrantLock eine benutzerdefinierte Sperre erstellt, die die Lock-Schnittstelle implementiert. Diese Sperre wird von den Threads verwendet, die erstellt wurden, um ausschließlich die Methode addIntArray() auszuführen.

Führen Sie diesen Code aus und stellen Sie sicher, dass die Ausgabe wie unten gezeigt ist.

Thread-0 ---> 0 + 1 = 1
Thread-0 ---> 1 + 2 = 3
Thread-0 ---> 3 + 3 = 6
Thread-0 ---> 6 + 4 = 10
Thread-0 ---> 10 + 5 = 15
Thread-1 ---> 0 + 1 = 1
Thread-1 ---> 1 + 2 = 3
Thread-1 ---> 3 + 3 = 6
Thread-1 ---> 6 + 4 = 10
Thread-1 ---> 10 + 5 = 15

Abschluss

In diesem Lernprogramm haben wir gelernt, wie Sie den Zugriff auf gemeinsam genutzte Ressourcen verwalten, indem wir behandelt haben, wie Sperren für diese Ressourcen erstellt werden. Wir haben behandelt, wie Interferenzen auftreten, wie Sperren mit der Funktion synchronized() verwendet werden und wie Sperren mit der Funktion withLock() verwendet werden.

Beachten Sie, dass der Unterschied zwischen diesen beiden Ansätzen darin besteht, dass die Funktion synchronized() nicht auf eine Sperrimplementierung beschränkt ist, die Funktion withLock() jedoch auf die Implementierung Lock.

David Mbochi Njonge avatar David Mbochi Njonge avatar

David is a back end developer with a major in computer science. He loves to solve problems using technology, learning new things, and making new friends. David is currently a technical writer who enjoys making hard concepts easier for other developers to understand and his work has been published on multiple sites.

LinkedIn GitHub