Kürzlich habe ich den Android Framework Layer -Code gelesen und die ThreadLocal -Klasse gesehen. Ich war ein wenig unbekannt, also habe ich nacheinander verschiedene verwandte Blogs gelesen. Ich studierte dann den Quellcode und stellte fest, dass mein Verständnis von den Blog -Posts, die ich zuvor gelesen habe, anders war. Deshalb habe ich beschlossen, einen Artikel zu schreiben, um über mein Verständnis zu sprechen, in der Hoffnung, dass er die folgende Rolle spielen kann:
- kann die Forschungsergebnisse löschen und Ihr Verständnis vertiefen.
- Es kann eine Rolle spielen, Jade anzuziehen und interessierten Studenten zu helfen, ihre Ideen zu klären.
- Teilen Sie Ihre Lernerfahrung und kommunizieren und lernen Sie mit allen.
1. Was ist ThreadLocal
ThreadLocal ist die grundlegende Klasse der Java -Klassenbibliothek unter dem Paket Java.lang;
Die offizielle Erklärung lautet wie folgt:
Implementiert einen Thread-lokalen Speicher, dh eine Variable, für die jeder Thread seinen eigenen Wert hat. Alle Threads teilen das gleiche ThreadLocal -Objekt, aber jeder sieht beim Zugriff einen anderen Wert, und Änderungen, die vor einem Thread vorgenommen wurden, wirken sich nicht auf die anderen Threads aus. Die Implementierung unterstützt Nullwerte.
Die allgemeine Bedeutung ist:
Der lokale Speichermechanismus von Fäden kann implementiert werden. Die ThreadLocal -Variable ist eine Variable, die in verschiedenen Threads unterschiedliche Werte aufweisen kann. Alle Threads können das gleiche ThreadLocal -Objekt freigeben, aber verschiedene Threads können beim Zugriff unterschiedliche Werte erhalten, und die Änderungen eines Threads betreffen andere Threads nicht. Klasse -Implementierungen unterstützen Nullwerte (Nullwerte können in SET- und GET -Methoden übergeben und zugegriffen werden).
Zusammenfassend gibt es drei Eigenschaften:
- Erhalten Sie unterschiedliche Werte, wenn Sie von verschiedenen Threads zugegriffen werden
- Alle Threadänderungen daran betreffen andere Threads nicht
- Unterstützen Sie NULL
Die folgenden Beispiele für diese Merkmale. Erstens definieren wir eine Testklasse. In dieser Klasse überprüfen wir die drei oben genannten Funktionen. Die Klassendefinition lautet wie folgt:
Test.java
public class test {// ThreadLocal Private Static ThreadLocal Name; public static void main (String [] args) löst Ausnahme aus {name = new ThreadLocal (); // Thread ithread a = new Thread () {public void run () {System.out.println ("Before Invoke Set, Value, Value:"+Name). A "); : "+name.get ());}}; // nicht aufrufen, drucken Sie den Wert nullSystem.out.println (name.get ()); // aufrufen set, um eine valuename.set (" Thread Main ”); // Start Thread Aa.start (); Thread bb.start (); b.join (); // Drucken Sie den Wert nach geändertem Wert durch Thread bsystem.out.println (name.get ())}}Codeanalyse:
Aus der Definition können wir sehen, dass nur ein ThreadLocal -Objekt deklariert wird, und die anderen drei Threads (Hauptfaden, Thread A und Thread B) dasselbe Objekt teilen; Anschließend wird auf den Wert des Objekts in verschiedenen Threads geändert und auf den Wert des Objekts in verschiedenen Threads zugegriffen, und das Ergebnis wird in der Konsole ausgegeben.
Siehe die Ergebnisse:
Aus den Ergebnissen der Konsolenausgabe können Sie feststellen, dass drei Nullausgänge darin enthalten sind. Dies liegt daran, dass das Objekt nicht vor der Ausgabe zugewiesen wurde, was das Merkmal der Unterstützung von Null überprüfte. Darüber hinaus kann festgestellt werden, dass ich in jedem Thread den Wert des Objekts geändert habe, aber wenn andere Threads auf das Objekt zugreifen, ist es nicht der geänderte Wert, sondern der Thread-lokale Wert. Dies überprüft auch die beiden anderen Funktionen.
2. Die Rolle von ThreadLocal
Jeder weiß, dass seine Nutzungsszenarien hauptsächlich mit Multi-Thread-Programmen sind. Wie sagen Sie das für seine spezifische Funktion? Ich denke, dies kann nur allgemein definiert werden, da die funktionalen Attribute eines Dinges das Denken aller einschränken, nachdem es definiert ist. Zum Beispiel werden Küchenmesser verwendet, um Gemüse zu schneiden, und viele Menschen werden es nicht verwenden, um Wassermelonen zu schneiden.
Hier werde ich über mein Verständnis seiner Funktion nur als Referenz sprechen und hoffe, dass es hilfreich sein wird. Schreiben wir es so. Wenn ein Multi-Thread-Programm einige Aufgaben der meisten Threads (dh einen Code in der Auslaufmethode) zusammenfassen muss, kann ThreadLocal verwendet werden, um die Variablen mit Thread-bezogenen Elementen in der Kapselung zu wickeln, um die Exklusivität des Thread-Zugriffs zu gewährleisten, und alle Threads können ein Einkapselungsobjekt freigeben. Sie können sich auf Looper in Android beziehen. Programmierer, die Probleme mit Code nicht beschreiben können, sind keine guten Programmierer.
Schauen Sie sich den Code an: ein Tool zur Angabe des zeitaufwändigen Code eines Threads (erstellt, um das Problem zu veranschaulichen)
StatisticCostTime.java
// Klasse, die die Kosten timatepublic class StatisticCostTime {// Startzeit aufzeichnen // private ThreadLocal starttime = new ThreadLocal (); private lange Startzeit; // Private ThreadLocal cost time = new ThreadLocal () privat lang Kosten; static class InstanceFactory {private statische endgültige statisticCostTime Instance = new StatisticCostTime ();} // startpublic void start () {// startTime.set (System. Nanotime ()); System.nanotime () - StartTime;} public Long getStime () {return start time; // return starttime.get ();} public long getCostTime () {// return costTime.get (); Rückgabekostenzeit;}Okay, das Tool-Design ist abgeschlossen. Jetzt zählen wir es, um die zeitaufwändigen Threads zu zählen und zu versuchen:
Main.java
public class main {public static void main (String [] args) löst eine Ausnahme aus {// Definieren Sie den Thread ithread a = neuer Thread () {public void run () {try {// Record TimestatisticCosttime.shareinstance (). Start (); System.out.println ("A-StartTime:"+StatisticCostTime.Shareinstance (). e) {}}}; // starten aa.start (); // Thread bThread b = neuer Thread () {public void run () {try {// Die Startzeit von B1StatisticCostTime.shareinstance (). consolesystem.out.println ("B1-StartTime:"+StatisticCosttime.shareinstance (). B1System.out.println ("B1:"+StatisticCosttime.shareinstance (). B2System.out.println ("B2-StartTime:"+StatisticCosttime.shareinstance (). B2System.out.println ("B2:"+StatisticCostTime.shareinstance (). GetCostTime ());} catch (Ausnahme E) {}}}; b.start ();}}Nach dem Ausführen des Codes lautet das Ausgabeergebnis wie folgt: Die Genauigkeit des Ausgabeergebnisses sind Nanosekunden.
Es hängt davon ab, ob sich das Ergebnis von dem unterscheidet, was wir erwartet haben. Ich fand heraus, dass das Ergebnis von A ungefähr B1+B2 entspricht. Wie kommt es, dass es so wird wie B2? Die Antwort ist, dass die ursprüngliche Absicht nicht gemeinsam genutzt werden sollte, wenn wir die Variablen für Starttime und Kosten definieren, aber ausschließlich für den Thread liegen. Hier werden die Variablen mit dem Singleton geteilt. Bei der Berechnung des Werts von A wurde die Starttime tatsächlich durch B2 modifiziert, sodass das gleiche Ergebnis wie B2 ausgegeben wird.
Öffnen wir nun den kommentierten Teil in StatisticCostTime und versuchen Sie es, indem Sie ihn in die Erklärung der ThreadLocal ändern.
Siehe die Ergebnisse:
Ah! Dies erreichte den erwarteten Effekt. Zu diesem Zeitpunkt würden einige Schüler sagen, dass dies kein Zugang zu Thread-Konzern ist, und kann ich die Sicherheit der Fäden sicherstellen, solange ich ThreadLocal verwende? Die Antwort lautet nein! Zunächst können wir herausfinden, warum es Probleme mit Fadensicherheit gibt, aber es gibt nur zwei Situationen:
1. Sie haben gemeinsame Ressourcen, die nicht unter Threads geteilt werden sollten.
2. Sie garantieren keinen geordneten Zugriff auf Ressourcen, die zwischen Threads geteilt werden.
Ersteres kann mithilfe von ThreadLocal durch die "räumliche Austauschzeit" -Methode gelöst werden (Sie können auch die lokalen Variablen der Threads direkt deklarieren), und letzteres kann durch "räumliche Austauschzeit" -Methode gelöst werden, was offensichtlich nicht das ist, was ThreadLokal kann.
3. Threadlokalprinzip
Das Implementierungsprinzip ist eigentlich sehr einfach. Jedes Mal, wenn der Lesen und Schreiben in das ThreadLocal -Objekt tatsächlich eine Lese- und Schreiboperation in das Werteobjekt des Threads ist; Hier klären wir, dass keine Kopie der Variablen erstellt wird, da der von der Variablen zugewiesene Speicherplatz nicht zum Speichern des T -Objekts verwendet wird, sondern die Werte des Threads zum Speichern des T -Objekts verwendet werden. Jedes Mal, wenn wir die ThreadLocal -Set -Methode im Thread aufrufen, ist es tatsächlich ein Prozess, das Objekt in das entsprechende Werteobjekt des Threads zu schreiben. Wenn Sie die ThreadLocal -GET -Methode aufrufen, ist es tatsächlich ein Prozess, das das Objekt aus dem entsprechenden Werteobjekt des Threads abzurufen.
Siehe den Quellcode:
ThreadLocal Member Variable Set
/*** Legt den Wert dieser Variablen für den aktuellen Thread fest. Wenn Sie auf * {@code null} gesetzt sind, wird der Wert auf NULL gesetzt und der zugrunde liegende Eintrag ist noch vorhanden. * * @param bewerten den neuen Wert der Variablen für den Anrufer -Thread. */public void set (t value) {thread currentThread = thread.currentThread (); Werte Werte = Werte (Stromthread); if (values == null) {values = initializeValues (currentThread); } Werte.put (this, value);}MITTRADLACE -MITGLIEDER METHODE ERHALTEN
/*** Gibt den Wert dieser Variablen für den aktuellen Thread zurück. Wenn ein Eintrag * für diese Variable in diesem Thread noch nicht vorhanden ist, erstellt diese Methode * einen Eintrag und füllt den Wert mit dem Ergebnis von * {@link #initialValue ()}. * * @return den aktuellen Wert der Variablen für den aufrufenden Thread. */@Unterdrücktwarnings ("deaktiviert") public t get () {// optimiert für den schnellen Pfad. Thread CurrentThread = thread.currentThread (); Werte Werte = Werte (Stromthread); if (values! = null) {Object [] table = values.table; int index = hash & values.mask; if (this.Reference == Tabelle [index]) {return (t) Tabelle [index + 1]; }} else {values = initializeValues (currentThread); } return (t) values.getAftermiss (this);}ThreadLocal Member -Methode InitializeValues
/*** Erstellt die Werteinstanz für diesen Thread und variablen Typ. */Values initializeValues (Threadstrom) {return current.localValues = new Values ();}ThreadLocal -Mitgliedsmethodenwerte
/*** Ruft die Werteinstanz für diesen Thread und variablen Typ ab. */Werte Werte (Threadstrom) {return curral.localValues;}Wie lesen und schreiben Sie Objekte in diese Werte?
Werte existieren als interne Klasse von Threadlocal; Diese Werte enthält ein wichtiges Array -Objekt [], das der wichtigste Teil der Beantwortung der Frage ist. Es wird verwendet, um verschiedene Arten von Laufquellenvariablen im Thread zu speichern. Die Frage ist also, wie Sie sicherstellen, dass Sie keine Werte anderer Typen erhalten, wenn Sie Variablen eines bestimmten Typs einnehmen? Im Allgemeinen wird eine Karte entsprechend dem Schlüsselwert zugeordnet; Ja, die Idee ist diese Idee, aber sie wird hier nicht mit Karte implementiert, sondern ein Kartenmechanismus, der mit einem Objekt [] implementiert ist. Wenn Sie jedoch MAP verwenden möchten, um sie zu verstehen, ist dies nicht möglich, da der Mechanismus derselbe ist. Der Schlüssel entspricht tatsächlich der schwachen Referenz von ThreadLocal, und der Wert entspricht dem Objekt, an dem wir übergeben wurden.
Lassen Sie uns erläutern, wie Objekt [] zur Implementierung des Kartenmechanismus verwendet wird (siehe Abbildung 1). Es verwendet die Parität des Array -Indexs, um Schlüssel und Wert zu unterscheiden, dh in der folgenden Tabelle speichert den Schlüssel an der gleichmäßigen Position und die ungerade Zahl speichert den Wert. So wird es gemacht. Wenn interessierte Schüler die Implementierung von Algorithmus kennen möchten, können sie sie ausführlich untersuchen. Ich werde sie hier nicht ausführlich erklären.
Basierend auf dem vorherigen ersten Beispiel wird die Speichersituation analysiert:
Wenn das Programm ausgeführt wird, gibt es drei Threads A, B und Main. Wenn name.set () im Thread aufgerufen wird, werden drei identische Speicherräume für drei Thread -Instanzen gleichzeitig im Haufenbereich zugewiesen, um das Werteobjekt zu speichern, wobei die Namensreferenz als Schlüssel verwendet wird, und das spezifische Objekt wird als Wert in drei verschiedenen Objekten [] (siehe Abbildung unten) gespeichert:
4. Zusammenfassung
ThreadLocal kann das Problem der Parallelität während der Multi-Thread-Programmierung nicht vollständig lösen. Dieses Problem erfordert auch unterschiedliche Lösungen, die nach verschiedenen Situationen, "Raum für Zeit" oder "Zeit für den Raum" ausgewählt werden müssen.
Die größte Funktion von ThreadLocal besteht darin, Thread-Shared-Variablen in Thread-lokale Variablen umzuwandeln, um die Isolierung zwischen Threads zu erreichen.
Im obigen dreht sich alles um schnelles Verständnis von Threadlocal in Java. Ich hoffe, es wird für alle hilfreich sein. Wenn es Mängel gibt, hinterlassen Sie bitte eine Nachricht, um darauf hinzuweisen. Vielen Dank an Freunde für Ihre Unterstützung für diese Seite.