Vorwort
Das Fadensicherheitsproblem des Multithreading ist subtil und unerwartet, da die Reihenfolge der Operationen in Multithreads ohne ordnungsgemäße Synchronisation unvorhersehbar ist. Mehrere Threads, die auf die gleiche gemeinsame Variable zugreifen, ist besonders anfällig für Parallelitätsprobleme, insbesondere wenn mehrere Threads eine gemeinsam genutzte Variable schreiben müssen, um die Sicherheit der Thread zu gewährleisten.
Im Allgemeinen müssen Benutzer beim Zugriff auf gemeinsame Variablen eine angemessene Synchronisation durchführen, wie in der folgenden Abbildung gezeigt:
Es ist ersichtlich, dass die Synchronisationsmaßnahme im Allgemeinen sperren wird, wodurch der Benutzer ein gewisses Verständnis des Schlosses verzeichnet, was offensichtlich die Belastung des Benutzers erhöht. Gibt es also einen Weg beim Erstellen einer Variablen, wenn jeder Thread auf sie zugreift, greift er auf die Variablen ihres eigenen Threads zu? Tatsächlich kann Threalocal das tun. Beachten Sie, dass die Entstehung von ThreadLocal die oben genannten Probleme nicht zu lösen scheint.
ThreadLocal ist im JDK -Paket bereitgestellt. Es liefert Thread-lokale Variablen. Wenn Sie eine ThreadLocal -Variable erstellen, hat jeder Thread, der auf diese Variable zugreift, eine lokale Kopie der Variablen. Wenn mehrere Threads diese Variable betreiben, betreiben sie die Variablen tatsächlich in ihrem eigenen lokalen Speicher, wodurch Probleme mit Thread -Sicherheit vermieden werden. Nach dem Erstellen einer Threadlokal -Variable,
Jeder Thread kopiert eine Variable in seinen lokalen Speicher, wie in der folgenden Abbildung gezeigt:
Okay, jetzt überlegen wir uns über eine Frage: Das Implementierungsprinzip von ThreadLocal und wie wird ThreadLocal intern als variable Thread -Isolationsmethode implementiert?
Zunächst müssen wir uns die Klassendiagrammstruktur von ThreadLocal ansehen, wie in der folgenden Abbildung gezeigt:
wie
Wie aus dem obigen Klassendiagramm zu sehen ist, gibt es in der Thread -Klasse einen Threadlocals und inheritablethreadlocals. Beide Arten von Variablen sind ThreadLocalMap, und ThreadLocalMap ist eine angepasste Hashmap. Standardmäßig sind beide Variablen in jedem Thread null. Sie werden nur erstellt, wenn der Thread zum ersten Mal den ThreadLocal -Satz aufruft oder die Methode abhält.
Tatsächlich werden die lokalen Variablen jedes Threads nicht in der ThreadLocal -Instanz gespeichert, sondern in der Threadlocals -Variablen des aufrufenden Threads gespeichert. Mit anderen Worten, lokale Variablen vom Typ ThreadLocal werden im spezifischen Thread -Speicherraum gespeichert.
ThreadLocal ist eigentlich eine Shell. Es gibt den Wertwert in die aufrufende Thread -Thread -Threadlocals über die Set -Methode und speichert ihn. Wenn der aufrufende Thread die GET -Methode aufruft, wird er aus der Threadlocals -Variablen des aktuellen Threads herausgenommen. Wenn der aufrufende Thread nicht endet, wird die lokale Variable in der Threadlocals -Variablen des aufrufenden Threads gespeichert.
Wenn Sie daher keine lokalen Variablen verwenden müssen, können Sie die lokale Variable aus der Threadlocals -Variablen des aktuellen Threads löschen, indem Sie die Methode der ThreadLocal -Variable entfernen. Einige Leute fragen sich vielleicht, warum Threadlocals als Kartenstruktur gestaltet werden? Es ist offensichtlich, dass jeder Thread mehrerer Threadlokalvariablen zugeordnet werden kann.
Als nächstes können wir den Quellcode in ThreadLocal eingeben, wie im folgenden Code gezeigt:
Es befasst sich hauptsächlich mit der Implementierungslogik der drei Methoden, die wie folgt festgelegt, erhalten und entfernen:
Schauen wir uns zuerst die SET -Methode (T var1) an
public void set (t var1) {// (1) den aktuellen Thread Thread var2 = thread.currentThread () abrufen; // (2) Der aktuelle Thread wird als Schlüssel verwendet, um die entsprechende Threadvariable zu finden. Wenn Sie gefunden werden, setzen Sie ThreadLocal.ThreadLocalMap var3 = this.getMap (var2); if (var3! = null) {var3.set (this, var1); } else {// (3) Der erste Aufruf wird erstellt, um den entsprechenden HashMap für den aktuellen Thread zu erstellen. Createmap (var2, var1); }}Wie oben erwähnt, erhalten Sie zuerst den aufrufenden Thread und verwenden Sie dann den aktuellen Thread als Parameter, um die Methode GetMap (var2) aufzurufen. Der Code getMap (Thread var2) lautet wie folgt:
ThreadLocal.ThreadLocalMap getMap (Thread var1) {return var1.threadlocals; }Es ist zu sehen, dass GetMap (Var2) die variablen Threadlocals des Threads erstellt und die ThreadLocal -Variable an die Elementvariable des Threads gebunden ist.
Wenn GETMAP (VAR2) nicht leer zurückgibt, setzen Sie den Wertwert auf Threadlocals, dh den aktuellen variablen Wert in die Speichervariablen -Threadlocals des aktuellen Threads. Threadlocals ist eine HashMap -Struktur, wobei der Schlüssel die Referenz des aktuellen ThreadLocal -Instanzobjekts ist und der Wert der Wert ist, der durch die festgelegte Methode übergeht.
Wenn GETMAP (VAR2) leer zurückgibt, bedeutet dies, dass die festgelegte Methode als erstes Mal aufgerufen wird und die Threadlocals -Variable des aktuellen Threads erstellt wird. Mal sehen, was in CreateMap (var2, var1) gemacht wird?
void createMap (Thread var1, t var2) {var1.Threadlocals = new ThreadLocal.ThreadLocalMap (this, var2); }Sie können sehen, dass die Threadlocals -Variable des aktuellen Threads erstellt wird.
Schauen wir uns als nächstes die Get () -Methode an, der Code lautet wie folgt:
public t get () {// (4) den aktuellen Thread Thread var1 = thread.currentThread () abrufen; // (5) das ThreadLocals Variable ThreadLocal.ThreadLocalMap var2 = this.getMap (var1) abrufen; // (6) Wenn ThreadLocals nicht null sind, wird der entsprechende lokale Variablenwert zurückgegeben, wenn (var2! if (var3! = null) {Objekt var4 = var3.Value; return var4; }} // (7) Wenn Threadlocals leer ist, wird die Threadlocals -Elementvariable des aktuellen Threads initialisiert. return this.setInitialValue (); }Code (4) Rufen Sie zuerst die aktuelle Threadinstanz ab. Wenn die Threadlocals -Variable des aktuellen Threads nicht null ist, wird die lokale Variable des aktuellen Threads direkt zurückgegeben. Andernfalls führen Sie den Code (7) aus, um zu initialisieren, und der Code von setInitialValue () lautet wie folgt:
private t SetInitialValue () {// (8) in Null -Objekt var1 = this.initialValue () initialisiert; Thread var2 = thread.currentThread (); ThreadLocal.ThreadLocalMap var3 = this.getMap (var2); // (9) Wenn die Threadlocals -Variable der aktuellen Threadvariablen nicht leer ist, wenn (var3! = Null) {var3.set (this, var1); // (10) Wenn die Threadlocals -Variable des aktuellen Threads leer ist} else {this.createMap (var2, var1); } return var1; }Wie oben erwähnt, wird der lokale variable Wert des aktuellen Threads auf null festgelegt, wenn die Threadlocals -Variable des aktuellen Threads nicht leer ist. Andernfalls wird CreateMap in die CreateMap -Variable des aktuellen Threads aufgerufen.
Als nächstes schauen wir uns die Void Remove () -Methode an, der Code lautet wie folgt:
public void remove () {threadLocal.threadlocalMap var1 = this.getMap (Thread.currentThread ()); if (var1! = null) {var1.remove (this); }}Wie oben erwähnt, wird die in der ThreadLocal -Instanz im aktuelle Thread angegebene lokale Variable, wenn die Threadlocals -Variable des aktuellen Threads nicht leer ist, gelöscht.
Schauen wir uns als nächstes die spezifische Demo an, der Code lautet wie folgt:
/*** Erstellt von Cong am 2018/6/3. */public class threadLocalTest {// (1) Druckenfunktion statische Hohlraumdruck (String Str) {//1.1 Drucken Sie den Wert der lokalvariablen Variablen im lokalen Speicher des aktuellen Thread -Systems. //1.2 Die lokalvariable Variable im lokalen Speicher des aktuellen Threads //LocalVariable.remove (); } // (2) Erstellen Sie ThreadLocal Variable Static ThreadLocal <String> localVariable = new ThreadLocal <> (); public static void main (String [] args) {// (3) Thread ein ThreadOne = neuer Thread (neuer Runnable () {public void run () {//3.1 Legen Sie den Wert lokaler variabler Lokalvariable in Thread One LocalVariable.set ("Lokale Variable des Threads 1"). System.out.println ("Ergebnis nach Entfernen der lokalen Variablen 1" + ":" + localVariable.get ()); // (4) Thread Thread Threadtwo = neuer Thread (neuer Runnable () {public void run () {//4.1 Legen Sie den Wert lokaler variabler localVariable in Thread ein localVariable.set ("Lokale Variable von Thread 2"); //4.2 Rufen Sie die Druckfunktion auf ("Thread 2 ------>; 2 " +": " + localVariable.get ());}}); // (5) starten Sie den ThreadOne.start (); threadTwo.start (); }}Code (2) erstellt eine ThreadLocal -Variable;
Codes (3) und (4) Erstellen Sie die Threads 1 bzw. 2;
Code (5) startet zwei Threads;
Code 3.1 In Thread 1 legt der Wert von LocalVariable über die festgelegte Methode fest. Diese Einstellung ist tatsächlich eine Kopie im lokalen Speicher von Thread 1. Diese Kopie kann nicht mit Thread 2 zugegriffen werden. Dann ruft Code 3.2 die Druckfunktion auf, und Code 1.1 erhält den Wert von LocalVariable im lokalen Speicher des aktuellen Threads (Thread 1) über die Get -Funktion;
Thread 2 führt ähnlich wie Thread 1 aus.
Die Betriebsergebnisse sind wie folgt:
Hier müssen wir auf das Gedächtnisleckproblem von ThreadLocal achten
Jeder Thread hat eine Mitgliedsvariable mit dem Namen Threadlocals im Inneren. Der variable Typ ist HashMap. Der Schlüssel ist dieser Verweis auf die von uns definierte ThreadLocal -Variable, und der Wert ist der Wert, wenn wir festgelegt sind. Die lokale Variable jedes Threads wird in den eigenen Speichervariablen -Threadlocals des Threads gespeichert. Wenn der aktuelle Thread nicht verschwindet, werden diese lokalen Variablen bis zur Aufbewahrung gespeichert.
Daher kann es zu Speicherleckagen führen. Nachdem Sie sie verwendet haben, sollten Sie die Methode von ThreadLocal entfernen, um die lokalen Variablen in Threadlocals des entsprechenden Threads zu löschen.
Nach dem Auspacken der Kommentare in Code 1.2 wird erneut ausgeführt, und das Laufergebnis lautet wie folgt:
Haben wir jemals über eine solche Frage nachgedacht: Erhalten wir den Wert der ThreadLocal -Variablen im übergeordneten Thread im untergeordneten Thread?
Hier können wir Ihnen feststellen, dass der Wert der im übergeordneten Thread festgelegten ThreadLocal -Variablen nicht im untergeordneten Thread erhalten werden kann. Gibt es also eine Möglichkeit, den untergeordneten Thread dazu zu bringen, im übergeordneten Thread auf den Wert zuzugreifen? InheritablethreadLocal entstand in der Zukunft. InheritablethreadLocal erben von ThreadLocal und bietet eine Funktion, auf die Kinder auf lokale Variablen zugreifen können, die im übergeordneten Thread eingestellt sind.
Gehen wir zunächst wie folgt zum Quellcode der InheritabletheadLocal -Klasse, wie folgt:
öffentliche Klasse InheritabletReadLocal <T> erweitert ThreadLocal <T> {public inHeritabletReadLocal () {} // (1) geschützt t ChildValue (t var1) {return var1; } // (2) ThreadLocalMap getMap (Thread var1) {return var1.inheritableTheadlocals; } // (3) void createMap (Thread var1, t var2) {var1.inheritabletReadlocals = new ThreadLocalMap (this, var2); }}Sie können sehen, dass die Erben von ThreadLocal und drei Methoden umgeschrieben werden. Der obige Code wurde markiert. Code (3) Es ist zu erkennen, dass die Inheritabletheadlocal die CreateMap -Methode neu schreibt. Wenn die SET -Methode zum ersten Mal aufgerufen wird, wird die Instanz der inheritableTheadlocals -Variablen des aktuellen Threads erstellt und nicht Threadlocals.
Code (2) Sie können wissen, dass die Inheritabletheadlocals erhalten wird, wenn die GET -Methode aufgerufen wird, um die interne Kartenvariable des aktuellen Threads zu erhalten, nicht Threadlocals.
Der wichtigste Punkt ist hier, wenn der umgeschriebene Code (1) ausgeführt wird und wie der untergeordnete Thread auf die lokalen Variablen des übergeordneten Threads zugreifen kann. Beginnend mit dem von Thread erstellten Code, dem Standardkonstruktor des Threads und dem Konstruktor der Thread.java -Klasse sind wie folgt:
/*** Erstellt von Cong am 2018/6/3. */ public thread (runnable target) {init (null, target, "thread-" + nextThreadnum (), 0); } private void init (Threadgroup G, Runnable -Ziel, String -Name, Long stacksize, AccessControlContext ACC) {// ... // (4) Erhalten Sie den aktuellen Thread Thread Parent = currentThead (); // ... // (5) Wenn die inheritableTheadlocals -Variable des übergeordneten Threads nicht null ist, wenn (parent.inheritableTheadlocals! this.stacksize = stacksize; Tid = nextThreadid (); }Beim Erstellen eines Threads wird die Init -Methode im Konstruktor aufgerufen. Wie bereits erwähnt, betreibt die InheritablethreadLocal -Klasse GET und die SET -Methode die variable InheritableTheadlocals, sodass die ineritableTheadlocal -Variable hier nicht null ist, sodass der Code (6) ausgeführt wird. Sehen wir uns den Quellcode der CreateInheritedMap -Methode wie folgt an:
static threadLocalMap createInheritedMap (ThreadLocalMap parentMap) {return New ThreadLocalMap (parentMap); }Sie können sehen, dass CreateInheritedMap die InheritabletheadLocals -Variable des übergeordneten Threads als Konstruktor verwendet, um eine neue ThreadLocalMap -Variable zu erstellen, und es dann der Inheritabletheadlocals -Variablen des untergeordneten Threads zugewiesen. Geben Sie dann den Konstruktor der ThreadLocalMap ein. Der Quellcode lautet wie folgt:
private threadLocalMap (ThreadLocalMap parentMap) {Eintrag [] parentTable = parentmap.table; int len = parentTable.length; SetThreshold (Len); Tabelle = neuer Eintrag [Len]; für (int j = 0; j <len; j ++) {Eintrag e = parentTable [j]; if (e! = null) {@Suppresswarnings ("deaktiviert") ThreadLocal <Obge. if (key! int h = key.threadlocalHashCode & (len - 1); while (table [h]! = null) h = nextIndex (h, len); Tabelle [H] = C; Größe ++; }}}}}}Der obige Code kopiert den Wert der InheritabletReadlocals -Mitgliedsvariable des übergeordneten Threads in das neue ThreadLocalMap -Objekt, und die von dem Code (7) ineritableAltheadLocal -Klasse (1) umgeschriebene Code (1) wird ebenfalls angezeigt.
Im Allgemeinen: Die InheritableTheadLocal -Klasse schreibt den Code (2) und (3) um und speichert die lokalen Variablen in die Variable "Inheritablethreadlocals des spezifischen Threads". Wenn der Thread die Variable durch die SET -Methode der Inheritabletheadlocals -Instanz festlegt, erstellt er die Variable der InheritabletheadLocals des aktuellen Threads. Wenn der übergeordnete Thread den untergeordneten Thread erstellt,
Der Konstruktor kopiert die lokale Variable in der Variablen "Inheritabletheadlocals im übergeordneten Thread" und kopiert sie in die Variable "InheritabletheadLocals des untergeordneten Threads".
Nachdem das Prinzip gut verstanden wurde, nutzen wir ein Beispiel, um zu überprüfen, was wir oben wissen, wie folgt:
Paket com.hjc;/*** Erstellt von Cong am 2018/6/3. */public class inheritabletHeadLocalTest {// (1) Thread Variable public static ThreadLocal <string> ThreadLocal = new ThreadLocal <string> (); public static void main (String [] args) {// (2) Setzen Sie Thread Variable ThreadLocal.set ("Hallo Java"); // (3) Child Thread Thread Thread = neuer Thread (neuer Runnable () {public void run () {// (4) Der Wert des untergeordneten Threads gibt das Thread -Variable System.out.println ("subthread:" + threadLocal.get ()) aus;}}); Thread.Start (); // (5) Der Haupt -Thread gibt das Thread Variable Value System.out.println ("übergeordnete Thread:" + threadLocal.get ()) aus; }}Die Betriebsergebnisse sind wie folgt:
Das heißt, nachdem dieselbe ThreadLocal -Variable im übergeordneten Thread festgelegt wurde, kann sie nicht im untergeordneten Thread erhalten werden. Gemäß der Einführung im vorherigen Abschnitt sollte dies ein normales Phänomen sein, da der aktuelle Thread ein untergeordnetes Thread ist, wenn der untergeordnete Thread die GET -Methode aufruft, und die festgelegte Methode aufgerufen wird, um die Thread -Variable festzulegen, ist der Haupt -Thread. Die beiden sind verschiedene Threads, und natürlich gibt der Kinderfaden beim Zugriff null zurück.
Gibt es also eine Möglichkeit, den untergeordneten Thread dazu zu bringen, im übergeordneten Thread auf den Wert zuzugreifen? Die Antwort lautet Ja, also verwenden Sie den von unserem oben genannten Prinzip analysierten inheritablethreadlocal.
Ändern Sie den Code (1) des obigen Beispiels an:
// (1) Thread Variable öffentliche statische ThreadLocal <string> ThreadLocal = new inheritablethreadLocal <string> ();
Die Betriebsergebnisse sind wie folgt:
Es ist ersichtlich, dass der Wertvariablenwert jetzt normal aus dem untergeordneten Thread erhalten werden kann. Unter welchen Umständen müssen Kinder Threadlokalvariablen aus dem übergeordneten Thread erhalten?
Es gibt ziemlich viele Situationen, wie die ThreadLocal -Variable, die Benutzeranmeldeinformationen speichert. Es ist sehr wahrscheinlich, dass die Unterthreads auch Benutzeranmeldeinformationen verwenden müssen. Beispielsweise muss einige Middleware eine einheitliche Tracking -ID verwenden, um den gesamten Anruflink aufzuzeichnen.
Verwendung von ThreadLocal in Spring Request Scope Scope Bean
Wir wissen, dass Sie beim Konfigurieren einer Bean in XML im Frühjahr das Bereichsattribut angeben können, um den Umfang der Bean als Singleton, Prototyp, Anforderung, Sitzung usw. zu konfigurieren. Das Implementierungsprinzip des Umfangs der Bean wird mit ThreadLocal implementiert. Wenn Sie möchten, dass eine Bohne in Ihrem Frühlingscontainer einen Umfang des Webs hat, hat
Zusätzlich zu den entsprechenden Umfangsattributen, die zur Konfiguration der Bean -Ebene erforderlich sind, muss es auch in Web.xml konfiguriert werden:
<Hörer> <Hörer-Klasse> org.springframework.web.context.request.requestContextListener </Listener-Klasse> </Listener>
Hier sehen wir uns hauptsächlich zwei Methoden von RequestContextListener an:
public void requestInitialized (servletRequestevent requestEvent)
Und
public void requestDestroyed (ServletRequestevent RequestEvent)
Wenn eine Webanforderung erfolgt, wird die RequestInitialized -Methode ausgeführt:
public void requestInitialized (servletRequestEvent requestEvent) {..... wegließen httpServletRequest Request = (httpServletRequest) RequestEvent.getServletRequest (); ServletRequestAttributes Attribute = new ServletRequestAttributes (Anfrage); request.setAttribute (request_attributes_attribute, Attribute); LocalContextHolder.setlocale (request.getlocale ()); // Setzen Sie das Attribut auf ThreadLocal Variable RequestContextHolder.setRequestAttributes (Attribute); } public static void setRequestAttributes (Anfrageattributes -Attribute) {setRequestAttributes (Attribute, False); } public static void setRequestAttributes (RequiredAttributes Attribute, boolean eröffnet) {if (Attribute == null) {ResetRequestAttributes (); } else {// default inheritable = false if (erdenbar) {inheritableSequestAttribUTSoDER.Set (Attribute); RequestAttributSholder.Remove (); } else {RequestAtTributShoder.set (Attribute); InheritableRequestAttributSholder.remove (); }}}Sie können den obigen Quellcode sehen. Da der Standardvererben falsch ist, werden unsere Attributwerte in RequestAttributeshoder platziert, und seine Definition lautet:
private statische endgültige ThreadLocal <FunschTributes> Anfrageableitungsholder = new namedThreadLocal <RequestAttributes> ("Anfrageattribute"); private statische endgültige ThreadLocal <FunschTributes> InheritableRequestAttributShoder = new NamedInheritabletReadLocal <FunschTributes> ("Anfragekontext");Unter ihnen erweitert benannte Threadlocal <t> ThreadLocal <T>, so dass es nicht vererbt werden kann.
Unter ihnen erweitert benannte Threadlocal <t> ThreadLocal <T>, so dass es nicht vererbt werden kann.
NameInheritabletReadLocal <T> erweitert die InheritabletheadLocal <T>, sodass es die Vererbung aufweist, sodass der in der RequestContexTHolder standardmäßig standardmäßige Attributwert nicht im untergeordneten Thread erhalten werden kann.
Wenn die Anforderung endet, wird die RequestDestroyed -Methode aufgerufen und der Quellcode wie folgt:
public void requestDestroyed (servletRequestEvent requestEvent) {ServletRequestAttributes Attributes = (ServletRequestAttributes) RequestEvent.getServletRequest (). getAtTribute (Request_attributes_attribute); ServletRequestattributes threadAttributes = (ServletRequestattributes) RequestContexTHolder.getRequestAttributes (); if (threadAttributes! } // Wenn die Anforderung endet, löschen Sie die Threadvariable des aktuellen Threads. LocalContextHolder.resetLocalContext (); RequestContextHolder.ResetRequestAttributes (); } if (Attribute! = null) {Attribute.RequestCompletEd (); }}Schauen wir uns als nächstes die Logik der Webanforderung an, die aus dem Timing -Diagramm aufgerufen wird:
Das heißt, jedes Mal, wenn eine Webanforderung vor der Verarbeitung des Kontextes (spezifische Anwendung) in Tomcat eingeleitet wird, wird die Eigenschaft RequestContexTHOLDER nach dem Host -Übereinstimmungen festgelegt, sodass der Anfrageabläufer nicht leer ist und am Ende der Anfrage gelöscht wird.
Standardmäßig kann auf die in der RequestContexTHolder platzierten Attribut -untergeordneten Threads nicht zugegriffen werden, und auf die Bohnen im Anforderungsbereich der Frühling werden mit ThreadLocal implementiert.
Als nächstes wird eine Beispiel -Simulationsanforderung durchgeführt, der Code lautet wie folgt:
Die Web.xml -Konfiguration lautet wie folgt:
Da es sich um einen Anforderungsumfang handelt, muss es sich um ein Webprojekt handeln und der RequestContextListener muss an web.xml konfiguriert werden.
<Hörer> <Hörer-Klasse> org.springframework.web.context.request.requestContextListener </Listener-Klasse> </Listener>
Dann in den IOC -Container eine Anforderungs -Scope -Bean einfügen. Der Code ist wie folgt:
<bean id = "requestbean" scope = "request"> <Eigenschaft name = "name" value = "hjc" /> <aop: Scoped-Proxy /> < /bean>
Der Testcode lautet wie folgt:
@Webresource ("/testService") public class testRPC {@autowired private requestBean requestInfo; @Resourcemapping ("test") public actionResult test (Fehlercontext -Kontext) {actionResult result = new ActionResult (); pvginfo.setName ("hjc"); String name = requestInfo.getName (); result.setValue (Name); Rückgabeergebnis; }}Wie oben erwähnt, konfigurieren Sie zuerst den RequestContextListener in Web.xml und injizieren Sie dann die RequestBean -Instanz in den IOC -Container mit dem Anforderungsbereich. Schließlich wird die RequestBean -Instanz in testRPC injiziert. Method -Test Erstens rufen die RequestInfo -Methode auf, um das Attribut des Namens festzulegen, dann das Namensattribut des Namens abzurufen und zurückzugeben.
Wenn das RequestInfo-Objekt Singleton ist, ist nach mehreren Threads die Testmethode gleichzeitig aufgerufen, jeder Thread ist ein SET-GET-Vorgang. Diese Operation ist nicht atomar und verursacht Fadensicherheitsprobleme. Der hier deklarierte Bereich ist die Anforderungsstufe, und jeder Thread verfügt über eine lokale Variable mit RequestInfo.
Das Zeitplan der oben genannten Beispielmethode ist wie folgt:
Wir müssen uns auf das konzentrieren, was passiert, wenn Sie Test aufrufen:
Tatsächlich ist der zuvor erstellte RequestInfo nach dem Proxy von CGGLIB (wenn Sie interessiert sind, Sie können ScopedProxyFactoryBean und andere Typen studieren). Wenn Sie also SETNAME oder GETNAME aufrufen, werden Sie von DynamicAdviseDInterceptor abgefangen. Der Interceptor ruft schließlich die GET -Methode von RequestScope auf, um die lokalen Variablen vom aktuellen Thread zu erhalten.
Der Schlüssel ist hier. Wir müssen sich den Quellcode des RequestScope -Handlungsmethode wie folgt ansehen:
öffentliches Objekt get (String -Name, ObjectFactory ObjectFactory) {RequestAtTributes Attributes = RequestContextHolder.CurrentRequestAttributes (); // (1) Objekt scopedObject = Attributes.GetAttribute (Name, getCope ()); if (ScopedObject == null) {ScopedObject = ObjectFactory.getObject (); // (2) Attribute.SetAttribute (Name, ScopedObject, getScope (); // (3)} return ScopedObject; }Es ist ersichtlich, dass der RequestAtTributSoder, wenn eine Anfrage initiiert wird, durch Aufrufen von RequestContextListener.requestinitialized in RequestContextListener.setRequestatTributess.
Nachdem die Anforderung an die Testmethode von testRPC weitergeleitet wurde, wird das erste Mal, dass die SetName -Methode in der Testmethode aufgerufen wird. Der Code in der GET-Methode (1) erhält den Wert des von der Thread-Lokal Variable RequestAttributSholder gespeicherten Attributssatzes über RequestContextListener.requestinitialized.
Überprüfen Sie dann, ob im Attributsatz ein Attribut mit dem Namen RequestInfo vorhanden ist. Da es sich um den ersten Anruf handelt, existiert er nicht, sodass der Code ausgeführt wird (2) und Spring ein RequestInfo -Objekt erstellen und ihn dann auf die Attribut -Set -Attribute festlegen lassen, dh er wird im lokalen Speicher des aktuellen Anforderungs -Threads gespeichert. Geben Sie dann das erstellte Objekt zurück und rufen Sie den SetName des erstellten Objekts auf.
Schließlich wird die GetName -Methode in der Testmethode aufgerufen, und die Methode RequestScope.get () wird aufgerufen. Der Code in der GET -Methode (1) erhält den Thread Lokale Variable RequestAttributes über RequestContextListener.requestinitialized festgelegt, und dann prüfen Sie, ob ein Attribut mit dem Namen RequestInfo im Attributsatz vorhanden ist.
Da die Bean mit dem Namen RequestInfo auf die ThreadLocal -Variable eingestellt wurde, wenn sie zum ersten Mal für SetName eingerufen wurde, und derselbe Thread für SetName und GetName aufgerufen wird, wird das RequestInfo -Objekt, das beim Aufrufen von SetName erstellt wurde, direkt zurückgegeben, und dann wird die GetName -Methode aufgerufen.
Bisher haben wir das Implementierungsprinzip von ThreadLocal verstanden und darauf hingewiesen, dass ThreadLocal die Vererbung nicht unterstützt. Dann haben wir sofort erklärt, wie inheritableTheadLocal das Feature, das ThreadLocal nicht unterstützt, die Vererbung kompensiert. Schließlich haben wir kurz vorgestellt, wie man ThreadLocal im Spring -Framework verwendet, um die Bean von Requews Scope zu implementieren.
Zusammenfassen
Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Referenzwert für das Studium oder die Arbeit eines jeden hat. Wenn Sie Fragen haben, können Sie eine Nachricht zur Kommunikation überlassen. Vielen Dank für Ihre Unterstützung bei Wulin.com.