Multithreading in Java

Die Java Virtual Machine unterstützt paralleles Rechnen . Alle Berechnungen können im Kontext eines oder mehrerer Threads durchgeführt werden. Wir können problemlos den Zugriff auf dieselbe Ressource oder dasselbe Objekt für mehrere Threads einrichten und einen Thread so einrichten, dass er einen einzelnen Codeblock ausführt.

Jeder Entwickler muss die Arbeit mit Threads während Lese- und Schreibvorgängen für Ressourcen synchronisieren, denen mehrere Threads zugewiesen sind.

Es ist wichtig, dass Sie zum Zeitpunkt des Zugriffs auf die Ressource über aktuelle Daten verfügen, damit ein anderer Thread diese ändern kann und Sie die aktuellsten Informationen erhalten. Selbst wenn wir das Beispiel eines Bankkontos nehmen, können Sie es nicht verwenden, bis das Geld dort eingegangen ist. Daher ist es wichtig, immer über aktuelle Daten zu verfügen. Java verfügt über spezielle Klassen zum Synchronisieren und Verwalten von Threads.

Thread-Objekte

Alles beginnt mit dem Hauptthread (Hauptthread), das heißt, Ihr Programm verfügt zumindest bereits über einen laufenden Thread. Der Hauptthread kann andere Threads mit Callable oder Runnable erstellen . Die Erstellung unterscheidet sich nur im Rückgabeergebnis. Runnable gibt kein Ergebnis zurück und kann keine geprüfte Ausnahme auslösen. Daher erhalten Sie eine gute Gelegenheit, eine effiziente Arbeit mit Dateien aufzubauen, aber dies ist sehr gefährlich und Sie müssen vorsichtig sein.

Es ist auch möglich, die Thread-Ausführung auf einem separaten CPU-Kern zu planen. Das System kann problemlos zwischen Threads wechseln und einen bestimmten Thread mit den richtigen Einstellungen ausführen: Das heißt, der Thread, der die Daten liest, wird zuerst ausgeführt. Sobald wir Daten haben, übergeben wir sie dann an den Thread, der für die Validierung verantwortlich ist. Danach übergeben wir es an den Thread, um einige Geschäftslogiken auszuführen, und ein neuer Thread schreibt sie zurück. In einer solchen Situation verarbeiten 4 Threads nacheinander Daten und alles funktioniert schneller als ein Thread. Jeder dieser Streams wird in einen nativen Betriebssystem-Stream konvertiert, aber wie er konvertiert wird, hängt von der JVM-Implementierung ab.

Die Thread- Klasse wird zum Erstellen und Arbeiten mit Threads verwendet. Es verfügt über Standardkontrollmechanismen sowie abstrakte Mechanismen wie Klassen und Sammlungen von java.util.concurrent .

Thread-Synchronisierung in Java

Die Kommunikation erfolgt durch die gemeinsame Nutzung des Zugriffs auf Objekte. Das ist zwar sehr effektiv, aber gleichzeitig kann man bei der Arbeit sehr leicht einen Fehler machen. Fehler treten in zwei Fällen auf: Thread-Interferenz – wenn ein anderer Thread Ihren Thread stört, und Speicherkonsistenzfehler – Speicherkonsistenz. Um diese Fehler zu beheben und zu verhindern, verfügen wir über verschiedene Synchronisierungsmethoden.

Die Thread-Synchronisierung in Java wird von Monitoren übernommen. Hierbei handelt es sich um einen Mechanismus auf hoher Ebene, der es jeweils nur einem Thread ermöglicht, einen Codeblock auszuführen, der durch denselben Monitor geschützt ist. Das Verhalten von Monitoren wird im Hinblick auf Sperren betrachtet; ein Monitor – ein Schloss.

Bei der Synchronisierung gibt es mehrere wichtige Punkte, auf die Sie achten müssen. Der erste Punkt ist der gegenseitige Ausschluss – nur ein Thread kann den Monitor besitzen, daher bedeutet die Synchronisierung auf dem Monitor, dass, sobald ein Thread einen durch den Monitor geschützten synchronisierten Block betritt, kein anderer Thread den durch den Monitor geschützten Block betreten kann. Dieser Monitor bis zum Der erste Thread verlässt den synchronisierten Block. Das heißt, mehrere Threads können nicht gleichzeitig auf denselben synchronisierten Block zugreifen.

Aber Synchronisierung bedeutet nicht nur gegenseitigen Ausschluss. Durch die Synchronisierung wird sichergestellt, dass Daten, die vor oder innerhalb eines synchronisierten Blocks in den Speicher geschrieben werden, für andere Threads sichtbar werden, die auf demselben Monitor synchronisiert werden. Nachdem wir den Block verlassen haben, geben wir den Monitor frei und ein anderer Thread kann ihn ergreifen und mit der Ausführung dieses Codeblocks beginnen.

Wenn ein neuer Thread den Monitor erfasst, erhalten wir Zugriff auf diesen Codeblock und können ihn ausführen. Zu diesem Zeitpunkt werden die Variablen aus dem Hauptspeicher geladen. Dann können wir alle Einträge sehen, die durch die vorherige Version des Monitors sichtbar gemacht wurden.

Ein Lese-/Schreibvorgang für ein Feld ist eine atomare Operation, wenn das Feld entweder als flüchtig deklariert oder durch eine eindeutige Sperre geschützt ist, die vor jedem Lese-/Schreibvorgang erworben wurde. Wenn jedoch immer noch ein Fehler auftritt, erhalten Sie eine Fehlermeldung zur Neuordnung (Änderung der Reihenfolge, Neuordnung). Es äußert sich in falsch synchronisierten Multithread-Programmen, bei denen ein Thread die Auswirkungen beobachten kann, die von anderen Threads erzeugt werden.

Der Effekt des gegenseitigen Ausschlusses und der Synchronisierung von Threads, dh ihr korrekter Betrieb, wird nur durch Eingabe eines synchronisierten Blocks oder einer synchronisierten Methode erreicht, die implizit eine Sperre erhält, oder durch explizite Erlangung einer Sperre. Wir werden weiter unten darüber sprechen. Beide Arbeitsweisen wirken sich auf Ihr Gedächtnis aus und es ist wichtig, die Arbeit mit flüchtigen Variablen nicht zu vergessen.

Flüchtige Felder in Java

Wenn eine Variable als volatile markiert ist , ist sie global verfügbar. Das heißt, wenn ein Thread auf eine flüchtige Variable zugreift, erhält er deren Wert, bevor er den Wert aus dem Cache verwendet.

Ein Schreibvorgang funktioniert wie eine Monitorfreigabe und ein Lesevorgang wie eine Monitorerfassung. Der Zugriff erfolgt in einer Relation vom Typ „zuvor durchgeführt“. Wenn Sie es herausfinden, ist alles, was für Thread A sichtbar ist, wenn er auf eine flüchtige Variable zugreift, die Variable für Thread B. Das heißt, dass Sie Ihre Änderungen von anderen Threads garantiert nicht verlieren.

Flüchtige Variablen sind atomar, d. h. beim Lesen einer solchen Variablen wird der gleiche Effekt wie beim Erlangen einer Sperre erzielt: Die Daten im Speicher werden für ungültig oder falsch erklärt und der Wert der flüchtigen Variablen wird erneut aus dem Speicher gelesen . Beim Schreiben wird die Auswirkung auf den Speicher genutzt, beim Aufheben einer Sperre wird ein flüchtiges Feld in den Speicher geschrieben.

Java gleichzeitig

Wenn Sie eine hocheffiziente Multithread-Anwendung erstellen möchten, müssen Sie die Klassen aus der JavaConcurrent- Bibliothek verwenden , die sich im Paket java.util.concurrent befinden .

Die Bibliothek ist sehr umfangreich und verfügt über unterschiedliche Funktionen. Werfen wir also einen Blick auf den Inhalt und teilen ihn in einige Module auf:

Java gleichzeitig

Concurrent Collections ist eine Reihe von Sammlungen für die Arbeit in einer Multithread-Umgebung. Anstelle des Basis-Wrappers Collections.synchronizedList mit blockierendem Zugriff auf die gesamte Sammlung werden Sperren für Datensegmente verwendet oder wartefreie Algorithmen zum parallelen Lesen von Daten.

Warteschlangen – nicht blockierende und blockierende Warteschlangen für die Arbeit in einer Multithread-Umgebung. Nicht blockierende Warteschlangen konzentrieren sich auf Geschwindigkeit und Betrieb, ohne Threads zu blockieren. Blockierungswarteschlangen eignen sich für die Arbeit, wenn Sie die Producer- oder Consumer -Threads „verlangsamen“ müssen. Wenn beispielsweise einige Bedingungen nicht erfüllt sind, ist die Warteschlange leer oder voll oder es gibt keinen freien Verbraucher .

Synchronizer sind Dienstprogramme zum Synchronisieren von Threads. Sie sind eine mächtige Waffe im „Parallel“-Computing.

Executors ist ein Framework für die bequemere und einfachere Erstellung von Thread-Pools. Es ist einfach, die Planung asynchroner Aufgaben einzurichten und Ergebnisse zu erhalten.

Sperren sind viele flexible Thread-Synchronisationsmechanismen im Vergleich zu den grundlegenden Mechanismen „ synced “, „ wait “, „notify “ und „notifyAll“ .

Atomics sind Klassen, die atomare Operationen an Grundelementen und Referenzen unterstützen können.