Threads teilen prozessweite Ressourcen wie Speicherhandles und Dateihandles, aber jeder Thread verfügt über einen eigenen Programmzähler (Programmzähler), Stapel, lokale Variablen usw.
Mehrere Threads im selben Programm können auch so geplant werden, dass sie gleichzeitig auf mehreren CPUs ausgeführt werden.
Der Hauptsynchronisationsmechanismus in Java ist das Schlüsselwort synchronisiert, das eine Exklusivität bietet Sperrmethode, aber der Begriff „Synchronisation“ umfasst auch flüchtige Typvariablen, explizite Sperren (Explicit Lock) und atomare Variablen.
Wenn keine ordnungsgemäße Synchronisierung verwendet wird, wenn mehrere Threads auf dieselbe veränderbare Statusvariable zugreifen, tritt im Programm ein Fehler auf. Es gibt drei Möglichkeiten, dieses Problem zu beheben:
Statusvariablen nicht zwischen Threads teilen.
Ändern Sie die Zustandsvariable in eine unveränderliche Variable.
Verwenden Sie die Synchronisierung beim Zugriff auf Zustandsvariablen.
Thread-Sicherheitsdefinition: Wenn mehrere Threads auf eine Klasse zugreifen, kann diese Klasse immer das richtige Verhalten zeigen, dann wird diese Klasse als Thread-sicher bezeichnet.
Zustandslose Objekte müssen Thread-sicher sein.
Die Essenz der meisten Rennbedingungen: ein Urteil fällen oder eine Berechnung auf der Grundlage einer möglicherweise ungültigen Beobachtung durchführen. Diese Art von Rennbedingung wird als „Prüfung vor der Ausführung“ bezeichnet: Zunächst wird beobachtet, dass eine Bedingung wahr ist (z. B. Datei und wenn Sie mit der Erstellung der Datei beginnen, kann die Beobachtung ungültig werden (ein anderer Thread hat Dateischäden verursacht usw.).
Angenommen, es gibt zwei Operationen A und B. Aus der Perspektive des Threads, der A ausführt, werden entweder alle B ausgeführt oder B wird überhaupt nicht ausgeführt, wenn ein anderer Thread B ausführt . , dann sind A und B atomar zueinander. Eine atomare Operation bedeutet, dass für alle Operationen, die auf denselben Zustand zugreifen (einschließlich der Operation selbst), diese Operation atomar ausgeführt wird.
In praktischen Situationen sollten vorhandene threadsichere Objekte (wie AtomicLong) so weit wie möglich verwendet werden, um den Status der Klasse zu verwalten. Im Vergleich zu nicht-threadsicheren Objekten ist es einfacher, den möglichen Status von Thread-sicheren Objekten und ihren Zustandsübergängen zu bestimmen, was die Aufrechterhaltung und Überprüfung der Thread-Sicherheit erleichtert.
Um die Zustandskonsistenz aufrechtzuerhalten, müssen alle relevanten Zustandsvariablen in einer einzigen atomaren Operation aktualisiert werden.
Eine Möglichkeit, Wiedereintritt zu implementieren, besteht darin, jeder Sperre einen Get-Count und einen Owner-Thread zuzuordnen. Wenn der Zählwert 0 ist, wird davon ausgegangen, dass die Sperre von keinem Thread gehalten wird. Wenn ein Thread eine Sperre anfordert, die nicht gehalten wird, zeichnet die JVM den Sperreninhaber auf und setzt den Erfassungszähler auf 1. Wenn derselbe Thread die Sperre erneut erhält, wird der Zähler erhöht, und wenn der Thread den synchronisierten Block verlässt, wird der Zähler entsprechend verringert. Wenn der Zähler 0 erreicht, wird die Sperre aufgehoben.
Nicht alle Daten müssen durch Sperren geschützt werden. Nur variable Daten, auf die mehrere Threads gleichzeitig zugreifen, müssen durch Sperren geschützt werden.
Achten Sie darauf, keine Sperren zu halten, wenn Sie lange Berechnungen oder Vorgänge durchführen, die möglicherweise nicht schnell abgeschlossen werden (z. B. Netzwerk-E/A oder Konsolen-E/A).
verstehe, denke ich, dass es sich um eine Mitgliedsvariable der Klasse handelt. Ein zustandsloses Objekt bedeutet, dass Mitgliedsvariablen keine Daten speichern können oder dass sie Daten speichern können, die Daten jedoch unveränderlich sind. Zustandslose Objekte sind threadsicher. Wenn die Methode eine Mitgliedsvariable enthält, müssen relevante Thread-sichere Vorgänge für diese Mitgliedsvariable ausgeführt werden.
Fügen Sie synchronisiert nicht blind vor der Methode hinzu. Dies kann die Thread-Sicherheit gewährleisten, aber die Parallelitätsfunktion der Methode wird geschwächt, was dazu führt, dass die Methode, die Parallelität unterstützen kann, blockiert wird. Dies führt zu einer Verlangsamung der Programmverarbeitungsgeschwindigkeit.
Der von synchronisiert umgebene Code sollte so kurz wie möglich sein, aber alle betroffenen Mitgliedsvariablen sollten zusammengehalten werden. Nicht verwandte Mitgliedsvariablen können von mehreren synchronisierten Variablen umgeben sein.
Die Bedeutung des Sperrens beschränkt sich nicht nur auf den gegenseitigen Ausschluss Sichtweite. Um sicherzustellen, dass alle Threads den neuesten Wert einer gemeinsam genutzten Variablen sehen, müssen alle Threads, die Lese- oder Schreibvorgänge ausführen, mit derselben Sperre synchronisiert werden.
Die Java-Sprache bietet einen etwas schwächeren Synchronisierungsmechanismus, nämlich flüchtige Variablen, um sicherzustellen, dass andere Threads über Variablenaktualisierungsvorgänge benachrichtigt werden. Wenn eine Variable als flüchtig deklariert wird, bemerken der Compiler und die Laufzeit, dass die Variable gemeinsam genutzt wird, und daher werden Operationen an der Variablen nicht mit anderen Speicheroperationen neu angeordnet. Flüchtige Variablen werden nicht in Registern zwischengespeichert oder sind auf andere Weise für andere Prozessoren unsichtbar. Daher wird beim Lesen einer Variablen vom flüchtigen Typ immer der zuletzt geschriebene Wert zurückgegeben.
Beim Zugriff auf flüchtige Variablen wird kein Sperrvorgang durchgeführt, sodass der Ausführungsthread nicht blockiert wird. Daher sind flüchtige Variablen ein einfacherer Synchronisierungsmechanismus als der synchronisierte Schlüsselwortmechanismus.
Flüchtige Variablen werden normalerweise verwendet, um den Abschluss eines Vorgangs, eine Unterbrechung oder einen Status zu kennzeichnen. Die Semantik von volatile reicht nicht aus, um die Atomizität des Inkrementierungsvorgangs (count++) sicherzustellen, es sei denn, Sie können sicherstellen, dass nur ein Thread den Schreibvorgang für die Variable ausführt.
Der Sperrmechanismus kann sowohl Sichtbarkeit als auch Atomizität gewährleisten, während flüchtige Variablen nur Sichtbarkeit gewährleisten können.
Eine flüchtige Variable sollte genau dann verwendet werden, wenn alle der folgenden Bedingungen erfüllt sind:
Schreibvorgänge in die Variable sind nicht zulässig. Hängen Sie vom aktuellen Wert der Variablen ab, oder Sie können sicherstellen, dass nur ein einzelner Thread den Wert der Variablen aktualisiert.
Diese Variable wird nicht zusammen mit anderen Zustandsvariablen in die Invarianzbedingung einbezogen.
Beim Zugriff auf diese Variable ist keine Sperre erforderlich.
„Veröffentlichen“ eines Objekts bedeutet, das Objekt für die Verwendung in Code außerhalb des aktuellen Bereichs verfügbar zu machen.
Wenn ein Objekt veröffentlicht wird, das nicht veröffentlicht werden sollte, wird diese Situation als Escape bezeichnet.
Lassen Sie dieses Blei während des Baus nicht entweichen.
Wenn Sie einen Ereignis-Listener registrieren oder einen Thread im Konstruktor starten möchten, können Sie einen privaten Konstruktor und eine öffentliche Factory-Methode verwenden, um Fehler im Konstruktionsprozess zu vermeiden.
Der Stapelschluss ist ein Sonderfall des Thread-Schließens. Beim Stapelschluss kann nur über lokale Variablen auf Objekte zugegriffen werden.
Eine standardisiertere Möglichkeit, den Thread-Abschluss aufrechtzuerhalten, ist die Verwendung von ThreadLocal. Diese Klasse kann einen Wert im Thread dem Objekt zuordnen, das den Wert enthält.
ThreadLocal-Objekte werden normalerweise verwendet, um zu verhindern, dass veränderbare Einzelinstanzobjekte (Singletons) oder globale Variablen gemeinsam genutzt werden.
Ein Objekt ist unveränderlich, wenn die folgenden Bedingungen erfüllt sind:
Der Zustand eines Objekts kann nach seiner Erstellung nicht geändert werden .
Alle Felder des Objekts sind vom endgültigen Typ.
Das Objekt wurde korrekt erstellt (diese Referenz ist während der Erstellung des Objekts nicht entwichen).
Unveränderliche Objekte müssen Thread-sicher sein.
Um ein Objekt sicher zu veröffentlichen, müssen die Referenz des Objekts und der Status des Objekts gleichzeitig für andere Threads sichtbar sein. Ein ordnungsgemäß konstruiertes Objekt kann sicher freigegeben werden durch:
Initialisieren einer Objektreferenz in einer statischen Initialisierungsfunktion.
Speichern Sie die Objektreferenz in einem flüchtigen Typfeld oder einem AtomicReferance-Objekt.
Speichern Sie die Referenz des Objekts im endgültigen Typfeld eines korrekt konstruierten Objekts.
Speichert einen Verweis auf das Objekt in einem durch eine Sperre geschützten Feld.
Ein de facto unveränderliches Objekt, das sicher veröffentlicht wird, kann von jedem Thread ohne zusätzliche Synchronisierung sicher verwendet werden.
Die Veröffentlichungsanforderungen eines Objekts hängen von seiner Veränderbarkeit ab:
Unveränderliche Objekte können durch jeden Mechanismus veröffentlicht werden.
Fakten, dass unveränderliche Objekte auf sichere Weise veröffentlicht werden müssen.
Veränderbare Objekte müssen auf sichere Weise freigegeben werden und müssen threadsicher oder durch eine Sperre geschützt sein.
Es gibt einige praktische Strategien, die Sie beim Verwenden und Teilen von Objekten in gleichzeitigen Programmen verwenden können, darunter:
Threads geschlossen. Ein in einem Thread eingeschlossenes Objekt kann nur einem Thread gehören. Das Objekt ist in diesem Thread eingeschlossen und kann nur von diesem Thread geändert werden.
Schreibgeschütztes Teilen. Ohne zusätzliche Synchronisierung können mehrere Threads gleichzeitig auf ein gemeinsam genutztes schreibgeschütztes Objekt zugreifen, es kann jedoch von keinem Thread geändert werden. Zu den gemeinsam genutzten schreibgeschützten Objekten gehören unveränderliche Objekte und de facto unveränderliche Objekte.
Thread-sicheres Teilen. Thread-sichere Objekte werden intern synchronisiert, sodass mehrere Threads ohne weitere Synchronisierung über die öffentliche Schnittstelle des Objekts auf sie zugreifen können.
Geschützte Objekte. Auf geschützte Objekte kann nur mit einer bestimmten Sperre zugegriffen werden. Zu den geschützten Objekten gehören Objekte, die in anderen Thread-sicheren Objekten gekapselt sind, sowie Objekte, die durch eine bestimmte Sperre freigegeben und geschützt sind.
Verständnis von Freigabe und Escape: Das heißt, Mitgliedsvariablen oder Objekte in einer Klasse können von anderen Klassen referenziert und verwendet werden wird freigegeben, z. B. eine statische Variable, die mit statisch geändert wurde, oder das Objekt der aktuell aufrufenden Methode. Escape bezieht sich auf das Problem, dass die Mitgliedsvariable oder das Objekt offengelegt und referenziert wird, obwohl mehrere Threads nicht darauf verweisen sollten, was dazu führt, dass der Wert falsch geändert wird. Mit einem Wort: Erweitern Sie nicht den Umfang einer Klasse und intern verwendeter Mitgliedsvariablen und -methoden. Dies ist auch ein Thema, das bei der Verpackung berücksichtigt werden sollte.
Dieser Escape: Das heißt, ein weiterer Thread wird in der inneren Klasse des Konstruktors gestartet, um auf dieses Objekt zu verweisen, aber zu diesem Zeitpunkt wurde das Objekt noch nicht erstellt, was dazu führen kann unerwarteter Fehler. Die Lösung besteht darin, eine Factory-Methode zu erstellen und dann den Konstruktor privat zu machen.
Die durch final geänderte Mitgliedsvariable muss im Konstruktor initialisiert werden, andernfalls kann der Mitgliedsvariablen nach der Instanziierung des Objekts kein Wert zugewiesen werden. Wenn die durch final geänderte Mitgliedsvariable auf ein Objekt verweist, kann die Adresse des Objekts nicht geändert werden, aber der Wert des Objekts kann geändert werden.
Verständnis der vier Möglichkeiten, ein Objekt sicher zu veröffentlichen, zum Beispiel gibt es einen Verweis auf Klasse B in Klasse A:
Die statische Initialisierungsmethode von A, z. B. public static A a = new A(b); In einer statischen Factory-Klasse wie dieser wird B initialisiert, wenn auf B verwiesen wird.
Die B-Mitgliedsvariable in Klasse A wird mit volatileb oder AtomicReferanceb geändert.
Die B-Mitgliedsvariable in Klasse A wird wie folgt mit finalem B b modifiziert.
Wenn die Methode in Klasse A B verwendet, ist sie von synchronisiert(lock){B...} umgeben.
Das einfache Verständnis unveränderlicher Objekte besteht darin, dass sie technisch veränderbar sind, aber während der Geschäftslogikverarbeitung nicht geändert werden.
Verwandte Empfehlungen:
Java-Thread-weite und gemeinsam genutzte Ressourcen
Java-Thread-Sicherheit und Unveränderlichkeit
Das obige ist der detaillierte Inhalt vonZusammenfassung der gleichzeitigen JAVA-Programmierung: Thread-Sicherheit, Objektfreigabe. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!