Synchronisierte Schlüsselwörter
Wenn ein Java -Sprachschlüsselwort verwendet wird, um eine Methode oder einen Codeblock zu ändern, kann sichergestellt werden, dass der Code höchstens ein Thread gleichzeitig ausführt.
Wenn zwei gleichzeitige Threads auf diesen synchronisierten (this) synchronisierten Codeblock in demselben Objektobjekt zugreifen, kann nur ein Thread innerhalb einer Zeit ausgeführt werden. Ein weiterer Thread muss warten, bis der aktuelle Thread diesen Codeblock ausführt, bevor er den Codeblock ausführen kann.
Wenn jedoch ein Thread auf einen synchronisierten (this) -Synchronisationscode-Code-Block eines Objekts zugreift, kann ein anderer Thread weiterhin auf den nicht synchronisierten (this) Synchronisationscodeblock in diesem Objekt zugreifen.
Es ist besonders wichtig, dass wenn ein Thread auf einen synchronisierten (this) -Synchronisationscode -Codeblock eines Objekts zugreift, andere Threads Zugriff auf alle anderen synchronisierten (this) -Synchronisationscodeblöcke im Objekt blockieren.
Das dritte Beispiel gilt auch für andere Synchroncodeblöcke. Das heißt, wenn ein Thread auf einen synchronisierten (this) -Synchronisationscode -Codeblock eines Objekts zugreift, wird die Objektsperrung dieses Objekts erhalten. Infolgedessen werden andere Threads auf alle synchronen Code -Teile des Objektobjekts zugreifen.
Die oben genannten Regeln gelten auch für andere Objektschlösser.
Codebeispiel
Paket test160118; public class testSynchronized {public static void main (String [] args) {sy = new Sy (0); SY2 = neuer Sy (1); Sy.Start (); Sy2.Start (); }} Klasse Sy erweitert Thread {private int Flag; statisches Objekt x1 = neues Objekt (); statisches Objekt x2 = neues Objekt (); public sy (int flag) {this.flag = flag; } @Override public void run () {System.out.println (Flag); try {if (flag == 0) {synchronized (x1) {System.out.println (Flag+"gesperrt x1"); Thread.sleep (1000); synchronisiert (x2) {System.out.println (Flag+"gesperrt x2"); } System.out.println (Flag+"Release x1 und x2"); }} if (flag == 1) {synchronized (x2) {System.out.println (Flag+"gesperrt x2"); Thread.sleep (1000); synchronisiert (x1) {System.out.println (Flag+"gesperrt x1"); } System.out.println (Flag+"Release x1 und x2"); }}} catch (interruptedException e) {e.printstacktrace (); }}}
ThreadLocal Lock-Free Thread Enconing Implementierungsprinzip
Was kann Threadlocal tun?
Dieser Satz ist schwer zu sagen. Werfen wir einen Blick auf einige der im eigentlichen Projekt aufgetretenen Schwierigkeiten: Wenn Sie einige Methoden gemäß einigen Parametern im Projekt aufrufen, rufen die Methode die Methode auf und rufen die Methode über Objekte hinweg über Objekte hinweg auf. Zu diesem Zeitpunkt müssen alle Parameter an B. übergeben werden. Wenn viele Methoden genannt werden, werden die Parameter immer mehr. Wenn das Programm Parameter hinzufügen muss, müssen Sie außerdem die entsprechenden Methoden nacheinander Parameter hinzufügen. Ja, es ist sehr problematisch. Ich glaube, Sie haben es begegnet. Dies sind auch einige gängige Verarbeitungsmethoden für objektorientierte in der C-Sprache. Unsere einfache Verarbeitungsmethode besteht jedoch darin, sie in ein Objekt einzuwickeln und einzugeben. Dieses Problem kann gelöst werden, indem die Attribute des Objekts hinzugefügt werden. Objekte sind jedoch normalerweise aussagekräftig, sodass manchmal eine einfache Objektverpackung einige erweiterte irrelevante Attribute hinzufügt, um unsere Klassendefinition sehr seltsam zu machen. In diesen Fällen, in denen wir komplexe Programme wie diesen strukturieren, verwenden wir einige ähnliche Bereiche, die dem Bereich ähnlich sind, um sie zu verarbeiten. Die Namen und Verwendungen sind häufiger. Ähnlich wie bei Webanwendungen gibt es Bereiche auf Kontext-, Sitzungs-, Anfrage- und Seitenebenen. ThreadLocal kann diese Art von Problem auch lösen, ist jedoch nicht sehr geeignet, um diese Art von Problem zu lösen. Wenn Sie sich diesen Problemen stellen, besteht es normalerweise nicht in den frühen Stadien von Umfang und Objekt und glaubt, dass Parameter nicht hinzugefügt werden. Als Hinzufügen von Parametern stellte ich fest, dass es viele Orte gibt, die geändert werden müssen. Um die Struktur des Codes nicht zu zerstören, ist es möglich, dass zu viele Parameter vorhanden sind, was die Lesbarkeit des Methodencodes verringert hat. ThreadLocal wird hinzugefügt, um es zu handhaben. Wenn beispielsweise eine Methode eine andere Methode aufruft, werden 8 Parameter übergeben, und einer der Parameter wird durch Aufrufen der n -ten Methodenschicht übergeben. Zu diesem Zeitpunkt muss die letzte Methode einen Parameter hinzufügen. Es ist natürlich, dass die erste Methode zu 9 Parametern wird, aber zu diesem Zeitpunkt werden die relevanten Methoden beteiligt, wodurch der Code aufgebläht wird.
Das oben genannte ThreadLocal ist ein Zweck, das Problem des Verlustes der Schafe zu reparieren, aber es ist keine besonders empfohlene Möglichkeit, es zu verwenden. Es gibt auch einige ähnliche Methoden, um es zu verwenden, dh auf der Rahmenebene gibt es viele dynamische Aufrufe, und einige Protokolle müssen während des Anrufprozesses erfüllt werden. Obwohl wir versuchen, universell zu sein, sind viele erweiterte Parameter bei der Definition des Protokolls nicht leicht zu berücksichtigen, und die Version wird auch jederzeit aktualisiert. Wenn das Framework jedoch verlängert wird, ist die Schnittstelle auch universell und rückständig. Wir brauchen einige erweiterte Inhalte, um bequem und einfach zu sein.
Einfach ausgedrückt, verwandelt ThreadLocal einige komplexe Systemverlängerungen in einfache Definitionen, wodurch die Teile, die an verwandten Parametern beteiligt sind, sehr einfach machen. Hier ist unser Beispiel:
Im Transaktionsmanager von Spring wird die von der Datenquelle erhaltene Verbindung in ThreadLocal platziert. Nach der Ausführung des Programms erfolgt die Verbindung von ThreadLocal und dann werden das Komitee und der Rollback durchgeführt. Bei der Verwendung ist es erforderlich, sicherzustellen, dass die vom Programm durch Datenquelle erhaltene Verbindung von der Feder erhalten wird. Warum eine solche Operation? Da die Geschäftsordnung vollständig von der Anwendung bestimmt wird und das Framework nicht verlangen kann, dass die Geschäftsordnung geschrieben wird, verliert das Framework den Vorteil, dass die Geschäftsordnung nicht zuzulassen, dass die Verbindung nicht verwaltet wird. Nachdem die Geschäftsordnung eingeschnitten wurde, wird die Frühling keine Verbindung zum Geschäftscode übergeben. Es muss an einem Ort gerettet werden. Wenn die darunter liegende Schicht durch Ibatis und Frühling verläuft. Wenn ein Framework wie JDBC die Verbindung derselben Datenquelle erhält, wird er nach den im Frühjahr vereinbarten Regeln eingerichtet, um sie zu erhalten. Da der Ausführungsprozess im selben Thread verarbeitet wird, wird die gleiche Verbindung hergestellt, um sicherzustellen, dass die verwendete Verbindung während des Commit-, Rollback- und Geschäftsbetriebs gleich ist, da nur derselbe Connecton Transaktionen garantieren kann, da sonst die Datenbank selbst nicht unterstützt wird.
In vielen gleichzeitigen Programmieranwendungen spielt ThreadLocal eine sehr wichtige Rolle. Es fügt keine Schlösser hinzu und schließt die Threads einfach nahtlos ein, ohne sich jedes Mal wie lokale Variablen zu erneuern. Da viele Leerzeichen mit Gewinde sicher sind, können sie wiederholt Thread-private Puffer verwenden.
Wie benutze ich ThreadLocal?
Definieren Sie eine ThreadLocal -Variable an einem geeigneten Ort im System, der als öffentlicher statischer Typ definiert werden kann (ein ThreadLocal -Objekt ist direkt neu). Wenn Sie Daten in sie einfügen möchten, verwenden Sie SET (Objekt), verwenden Sie GET () -Operation und verwenden Sie REME () beim Löschen von Elementen. Die anderen Methoden sind nicht öffentliche Methoden und werden nicht empfohlen.
Hier ist ein einfaches Beispiel (Code -Snippet 1):
public class threadLocalTest2 {öffentliches endgültiges statisches ThreadLocal <string> test_thread_name_local = new ThreadLocal <string> (); public Final Static ThreadLocal <string> test_thread_value_local = new ThreadLocal <string> (); public static void main (String [] args) {für (int i = 0; i <100; i ++) {Final String name = "Thread- [" + i + "]"; Final String value = string.Valueof (i); neuer Thread () {public void run () {try {test_thread_name_local.set (name); Test_thread_value_local.set (value); Calla (); } endlich {test_thread_name_local.remove (); Test_thread_value_local.remove (); } } } }.Start(); }} public static void calla () {callb (); } public static void callb () {new ThreadLocalTest2 (). callc (); } public void callc () {calld (); } public void calld () {System.out.println (test_thread_name_local.get () + "/t =/t" + test_thread_value_local.get ()); }}Hier simulieren wir 100 Threads, um auf den Namen und den Wert zugreifen zu können. Die Werte von Namen und Wert werden absichtlich in der Mitte auf dasselbe festgelegt, um festzustellen, ob ein Parallelitätsproblem vorliegt. Durch die Ausgabe können wir sehen, dass der Thread -Ausgang nicht in der Reihenfolge ausgegeben wird, was bedeutet, dass er parallel ausgeführt wird und der Thread -Name und der Wert entsprechend sein können. In der Mitte werden durch mehrere Methoden die Parameter nicht im tatsächlichen Aufruf übergeben. So erhalten Sie die entsprechenden Variablen. In den tatsächlichen Systemen überqueren sie jedoch häufig Klassen. Hier werden sie nur in einer Klasse simuliert. Tatsächlich sind Kreuzklassen das gleiche Ergebnis. Sie können sie selbst simulieren.
Ich glaube, dass viele Programmierer, nachdem sie dies gesehen haben, sehr an dem Prinzip des Threadlocals interessiert sind. Mal sehen, wie es gemacht wird. Obwohl die Parameter nicht übergeben werden, können sie sie wie lokale Variablen verwenden. Es ist in der Tat ziemlich magisch. Tatsächlich können Sie erkennen, dass es sich um eine Einstellungsmethode handelt. Wenn Sie sehen, dass der Name mit dem Thread in Verbindung gebracht werden sollte, dann schauen wir uns den Quellcode an. Da wir am meisten eingestellt sind, holen Sie sich und entfernen Sie und beginnen Sie mit dem SET:
Die SET (T OBJ) -Methode lautet (Code -Snippet 2):
public void set (t value) {thread t = thread.currentThread (); ThreadLocalMap map = getMap (t); if (map! = null) map.set (this, Wert); sonst CreateMap (t, Wert);}Zuerst wird der aktuelle Thread erhalten, das gleiche wie die Vermutung, und dann gibt es eine GetMap -Methode, die im aktuellen Thread weitergeht. Wir können zunächst verstehen, dass diese Karte eine Karte ist, die sich auf den Thread bezieht. Wenn es nicht leer ist, machen Sie als nächstes den festgelegten Betrieb. Wenn Sie es folgen, werden Sie feststellen, dass dies der Put -Operation von HashMap ähnelt, dh ein Datenstück wird in die Karte geschrieben. Wenn es leer ist, wird die CreateMap -Methode aufgerufen. Schauen Sie nach dem Eingang nach (Code -Snippet 3):
void createMap (Thread t, t FirstValue) {t.Threadlocals = new ThreadLocalMap (this, FirstValue);};};Cashback erstellt eine ThreadLocalMap und schreibt die bestandenen Parameter und aktuellen Threadlocal als KV -Struktur (Code -Snippet 4):
ThreadLocalMap (ThreadLocal FirstKey, Object FirstValue) {table = new Entry [initial_capacity]; int i = FirstKey.ThreadLocalHashCode & (initial_capacity - 1); Tabelle [i] = neuer Eintrag (FirstKey, FirstValue); Größe = 1; setThreshold (initial_capacity);}Dies wird hier nicht erklärt, die strukturellen Details von ThreadLocalMap. Sie müssen nur wissen, dass seine Implementierung HashMap ähnelt. Es gibt viele Methoden, bei denen keine Implementierungskarte vorhanden ist, da Sie nicht möchten, dass Sie eine Karte durch einige Methoden (z. B. Reflexion) erhalten, um sie weiter zu betreiben. Es ist eine statische innere Klasse im ThreadLocal -Standardtyp, und nur Klassen unter java.lang können sich darauf beziehen, sodass Sie sich darauf vorstellen können, dass Thread sich darauf beziehen kann.
Schauen wir zurück auf die GetMap -Methode, da ich nur weiß, dass sich die erhaltene Karte mit dem Thread bezieht, und über Code -Snippet 3 gibt es eine T.ThreadlocalMap = new ThreadLocalMap (dieses, FirstValue). Ich glaube, Sie sollten wahrscheinlich verstehen, dass diese Variable von Thread kommen sollte. Gehen wir gemäß der GetMap -Methode ein:
ThreadLocalMap getMap (Thread t) {return t.Threadlocals;}Ja, es stammt aus Thread und dieser Thread ist zufällig der aktuelle Thread. Gehen Sie also in die Definition:
ThreadLocal.ThreadLocalMap ThreadLocals = null;
Diese Eigenschaft befindet sich in der Thread-Klasse, dh jeder Thread verfügt standardmäßig über eine ThreadLocalMap, mit der lokale Variablen auf Thread-Ebene gespeichert werden. Normalerweise können Sie ihnen keine Werte zuweisen, da solche Zuordnungen normalerweise unsicher sind.
Es scheint ein bisschen chaotisch zu sein, mach dir keine Sorgen, lass uns zurückblicken und die Ideen erkunden:
1. Es gibt eine Eigenschaft in Thread, die HashMap ähnelt, aber sein Name ist ThreadLocalMap. Diese Eigenschaft ist vom Standardtyp, sodass auf alle Klassen unter demselben Paket verwiesen werden können. Da es sich um eine lokale Variable des Threads handelt, hat jeder Thread eine eigene separate Karte, die nicht miteinander in Konflikt steht. Selbst wenn ThreadLocal als statische Threads definiert wird, gibt es keinen Konflikt.
2. ThreadLocal und Thread befinden sich unter demselben Paket. Sie können sich auf diese Klasse beziehen und darauf arbeiten. Zu diesem Zeitpunkt definiert jeder ThreadLocal eine, verwenden Sie dies als Schlüssel und den Wert, den Sie als Wert übergeben, und dies ist der ThreadLocal, den Sie definieren. Daher verwenden unterschiedliche Threadlokalvariablen ein Satz, und die Daten untereinander konflikt nicht, da ihre Schlüssel unterschiedlich sind. Nach dem gleichen ThreadLocal wird das letzte Mal das letzte Mal die vorherrschenden Operationen sein.
3. Wenn Threads parallel sind, kann ThreadLocal wie lokale Variablen verwendet und thread-sicher sind, und die Daten zwischen verschiedenen Threadlokal-Variablen haben keinen Konflikt.
Betrachten wir weiterhin die GET -Methode und entfernen Sie die Methode, sie ist tatsächlich einfach:
public t get () {Thread t = thread.currentThread (); ThreadLocalMap map = getMap (t); if (map! if (e! = null) return (t) E. value; } return setInitialValue ();}Wenn Sie die GetMap -Methode gemäß dem aktuellen Thread aufrufen, dh T. ThreadlocalMap wird aufgerufen, und dann in der Karte zu suchen, beachten Sie, dass die Karte mit dem Eintrag gefunden wird, dh die Grundstruktur von KV, da Sie nur in den Wert schreiben, und ein E.wert, um den Wert zurückzugeben, den Sie geschrieben haben, da der Schlüssel selbst den Schlüssel ist. Sie können sehen, dass MAP.GetEntry auch dadurch erhalten wird.
Die gleiche Entfernungsmethode ist:
public void remove () {ThreadLocalMap m = getMap (Thread.CurrentThread ()); if (m! = null) m.remove (this);}Außerdem wird die Karte basierend auf dem aktuellen Thread erhalten. Wenn es nicht leer ist, entfernen Sie und entfernen Sie dies.
Was sind zusätzlich (2013-6-29) die Fallstricke des Vergessens zu schreiben? Welche Fallstricke gibt es in diesem Threadlocal? Aus dem vorherigen Beispiel sollten Sie sehen, dass das ThreadLocal-bezogene Objekt an eine Karte gebunden ist und diese Karte eine Eigenschaft des Thread-Threads ist. Dann gibt es ein Problem, dass, wenn Sie sich nicht selbst entfernen oder nicht wissen, wann Sie in Ihrem eigenen Programm entfernen sollen, der Thread nicht angemeldet ist und der Datensatz in nicht angemeldet wird.
Andererseits, es sei denn, Sie verstehen klar, wo dieses Objekt festgelegt werden soll und wo Sie entfernen sollen. Wenn es vage ist, ist es sehr wahrscheinlich, dass Ihr Code nicht in die Position entfernen oder logische Probleme verursacht. Wenn es nicht entfernt wird, müssen Sie darauf warten, dass der Thread angemeldet ist. In vielen Anwendungsservern werden Threads wiederverwendet, da in den Kernel -Allokationsthreads immer noch Overhead vorhanden sind. Daher ist es schwierig, dass Threads in diesen Anwendungen angemeldet werden. Dann ist die an ThreadLocal geschriebenen Daten natürlich nicht einfach ausgelogt. Diese können versehentlich versteckt und verwendet werden, wenn wir einige Open Source -Frameworks verwenden, was zu Problemen führen kann. Schließlich stellte ich fest, dass bei OOM die Daten tatsächlich von ThreadLocalMap stammen. Ich weiß nicht, wo diese Daten festgelegt sind, daher sollten Sie auf diese Grube achten, und mehr als eine Person ist in diese Grube gefallen.