Préface
Le problème de sécurité du fil du multithreading est subtil et inattendu, car l'ordre des opérations en multithreads est imprévisible sans synchronisation appropriée. Plusieurs threads accès à la même variable partagée sont particulièrement sujets aux problèmes de concurrence, en particulier lorsque plusieurs threads doivent écrire une variable partagée, afin d'assurer la sécurité des threads,
Généralement, les utilisateurs doivent effectuer une synchronisation appropriée lors de l'accès à des variables partagées, comme indiqué dans la figure ci-dessous:
On peut voir que la mesure de synchronisation est généralement le verrouillage, ce qui nécessite que l'utilisateur ait une certaine compréhension de la serrure, ce qui augmente évidemment la charge de l'utilisateur. Y a-t-il donc un moyen de créer une variable, lorsque chaque thread y accède, il accède aux variables de son propre thread? En fait, Thalalocical peut le faire. Notez que l'émergence du threadlocal ne semble pas résoudre les problèmes ci-dessus.
ThreadLocal est fourni dans le package JDK. Il fournit des variables de filetage locales. Autrement dit, si vous créez une variable threadlocal, chaque thread accédant à cette variable aura une copie locale de la variable. Lorsque plusieurs threads fonctionnent cette variable, ils fonctionnent en fait les variables dans leur propre mémoire locale, évitant ainsi les problèmes de sécurité des fils. Après avoir créé une variable threadlocale,
Chaque thread copiera une variable dans sa mémoire locale, comme indiqué dans la figure ci-dessous:
D'accord, réfléchissons maintenant à une question: le principe de mise en œuvre de ThreadLocal, et comment est-il mis en œuvre en tant qu'implémentée en tant que méthode d'isolement de threads variables, en interne?
Tout d'abord, nous devons examiner la structure du diagramme de classe du threadlocal, comme indiqué dans la figure suivante:
comme
Comme on peut le voir dans le diagramme de classe ci-dessus, il y a un threadLocal et HéritertHreadLocals dans la classe de threads. Les deux types de variables sont ThreadLocalmap et ThreadLocalmap est un hashmap personnalisé. Par défaut, les deux variables dans chaque thread sont nulles. Ils ne seront créés que lorsque le thread appelle le jeu de threadlocal ou d'obtenir la méthode pour la première fois.
En fait, les variables locales de chaque thread ne sont pas stockées dans l'instance threadlocal, mais sont stockées dans la variable ThreadLocals du thread d'appel. En d'autres termes, les variables locales de type threadlocal sont stockées dans l'espace de mémoire de thread spécifique.
ThreadLocal est en fait une coquille. Il met la valeur de valeur dans le thread thread d'appel ThreadLocals via la méthode SET et le stocke. Lorsque le thread d'appel appelle sa méthode GET, il est retiré de la variable ThreadLocals du thread actuel. Si le thread d'appel ne se termine pas, la variable locale sera stockée dans la variable ThreadLocals du thread d'appel.
Par conséquent, lorsque vous n'avez pas besoin d'utiliser des variables locales, vous pouvez supprimer la variable locale de la variable ThreadLocals du thread actuel en appelant la méthode de suppression de la variable ThreadLocal. Certaines personnes peuvent se demander pourquoi ThreadLocals est conçu comme une structure de carte? Il est évident que chaque thread peut être associé à plusieurs variables threadlocal.
Ensuite, nous pouvons saisir le code source dans ThreadLocal comme indiqué dans le code suivant:
Il examine principalement la logique d'implémentation des trois méthodes définies, obtenez et supprimez, comme suit:
Regardons d'abord la méthode Set (t var1)
public void set (t var1) {// (1) obtenir le thread actuel var2 = thread.currentThread (); // (2) Le thread actuel est utilisé comme touche pour trouver la variable de thread correspondante. S'il est trouvé, définissez ThreadLocal.ThreadLocalmap var3 = this.getMap (var2); if (var3! = null) {var3.set (this, var1); } else {// (3) Le premier appel est créé pour créer le hashmap correspondant pour le thread actuel this.createMap (var2, var1); }}Comme mentionné ci-dessus, le code (1), obtenez d'abord le thread d'appel, puis utilisez le thread actuel comme paramètre pour appeler la méthode GetMap (var2). Le code GetMap (Thread Var2) est le suivant:
ThreadLocal.ThreadLocalmap getMap (Thread var1) {return var1.ThreadLocals; }On peut voir que ce que fait GetMap (var2) est d'obtenir la variable du threads du thread, et la variable threadlocal est liée à la variable membre du thread.
Si GetMap (var2) ne revient pas vide, puis définissez la valeur de valeur sur ThreadLocals, c'est-à-dire, placez la valeur de variable actuelle dans la variable de mémoire ThreadLocals du thread actuel. ThreadLocals est une structure HashMap, où la clé est la référence de l'objet d'instance ThreadLocal actuel, et la valeur est la valeur transmise via la méthode SET.
Si getmap (var2) renvoie vide, cela signifie que la méthode de définition est appelée la première fois, puis la variable ThreadLocals du thread actuel est créée. Voyons ce qui est fait dans CreateMap (var2, var1)?
void CreateMap (Thread var1, t var2) {var1.ThreadLocals = new ThreadLocal.ThreadLocalmap (this, var2); }Ce que vous pouvez voir est de créer la variable ThreadLocals du thread actuel.
Ensuite, regardons la méthode get (), le code est le suivant:
public t get () {// (4) Obtenez le thread actuel thread var1 = thread.currentThread (); // (5) Obtenez la variable threadLocals ThreadLocal.ThreadLocalmap var2 = this.getMap (var1); // (6) Si ThreadLocals n'est pas nul, la valeur de variable locale correspondante est renvoyée if (var2! = Null) {ThreadLocal.ThreadLocalmap.Entry var3 = var2.GetTentry (this); if (var3! = null) {objet var4 = var3.Value; return var4; }} // (7) Si ThreadLocals est vide, la variable de membre ThreadLocals du thread actuel est initialisée. Renvoie ce.SetInitialValue (); }Code (4) Obtenez d'abord l'instance de thread actuelle. Si la variable ThreadLocals du thread actuel n'est pas nul, elle renverra directement la variable locale du thread actuel. Sinon, exécuter le code (7) pour initialiser, et le code de setInitialValue () est le suivant:
private t setInitialValue () {// (8) initialisé à l'objet nul var1 = this.InitialValue (); Thread var2 = thread.currentThread (); ThreadLocal.ThreadLocalmap var3 = this.getMap (var2); // (9) Si la variable ThreadLocals de la variable de thread actuelle n'est pas vide if (var3! = Null) {var3.set (this, var1); // (10) Si la variable threadLocals du thread actuel est vide} else {this.createMap (var2, var1); } return var1; }Comme mentionné ci-dessus, si la variable ThreadLocals du thread actuel n'est pas vide, la valeur de variable locale du thread actuel est définie sur NULL. Sinon, CreateMap est appelé à la variable CreateMap du thread actuel.
Ensuite, nous examinons la méthode void reous (), le code est le suivant:
public void retire () {ThreadLocal.ThreadLocalmap var1 = this.getMap (Thread.currentThread ()); if (var1! = null) {var1.Remove (this); }}Comme mentionné ci-dessus, si la variable ThreadLocals du thread actuel n'est pas vide, la variable locale spécifiée dans l'instance ThreadLocal dans le thread actuel est supprimée.
Ensuite, jetons un coup d'œil à la démo spécifique, le code est le suivant:
/ ** * Créé par Cong le 2018/6/3. * / classe publique ThreadLocalTest {// (1) Fonction d'impression statique void print (String str) {//1.1 Imprimez la valeur de la variable localeVariable dans la mémoire locale du thread actuel System.out.println (str + ":" + localVariable.get ()); //1.2 Effacer la variable localeVariable dans la mémoire locale du thread actuel //localvariable.remove (); } // (2) Créer une variable threadlocal statique ThreadLocal <string> localVariable = new ThreadLocal <> (); public static void main (String [] args) {// (3) Créer Thread One Thread ThreadOne = new Thread (new Runnable () {public void run () {//3.1 Définissez la valeur de la variable locale localVariable dans le thread One localVariable.Set ("Variable locale de Thread 1"); //3.2 Appelez la valeur de printe System.out.println ("Résultat après avoir supprimé la variable locale du thread 1" + ":" + localVariable.get ());}}); // (4) Créer Thread Two Thread ThreadTwo = new Thread (new Runnable () {public void run () {//4.1 Définissez la valeur de la variable locale localVariable dans le thread One localVariable.Set ("Variable locale du thread 2"); //4.2 Appelez la fonction d'impression Print ("Thread 2 ----->"); //4.3 Imprimer la variable locale System.out.out. 2 "+": "+ localVariable.get ());}}); // (5) Démarrez le thread threadOne.start (); threadtwo.start (); }}Le code (2) crée une variable threadlocale;
Les codes (3) et (4) créent des threads 1 et 2 respectivement;
Le code (5) démarre deux threads;
Le code 3.1 dans le thread 1 définit la valeur de localVariable via la méthode définie. Ce paramètre est en fait une copie dans la mémoire locale du thread 1. Cette copie ne peut pas être accessible par le thread 2. Puis le code 3.2 appelle la fonction d'impression et le code 1.1 obtient la valeur de localVariable dans la mémoire locale du thread actuel (Thread 1) via la fonction Get;
Le thread 2 s'exécute similaire au thread 1.
Les résultats de l'opération sont les suivants:
Ici, nous devons prêter attention au problème de la fuite de mémoire du threadlocal
Chaque thread a une variable de membre nommée ThreadLocals à l'intérieur. Le type variable est HashMap. La clé est cette référence à la variable threadLocale que nous avons définie, et la valeur est la valeur lorsque nous définissons. La variable locale de chaque thread est stockée dans la propre variable de mémoire du thread. Si le thread actuel ne disparaît pas, ces variables locales seront stockées avant.
Par conséquent, il peut provoquer une fuite de mémoire, donc après l'avoir utilisé, n'oubliez pas d'appeler la méthode de suppression de ThreadLocal pour supprimer les variables locales dans les filetages du thread correspondant.
Après le déballage des commentaires dans le code 1.2, exécutez à nouveau et le résultat d'exécution est le suivant:
Avons-nous déjà pensé à une question comme celle-ci: obtenons-nous la valeur de la variable threadlocal définie dans le thread parent dans le thread enfant?
Ici, nous pouvons vous dire que la valeur de la variable threadlocal définie dans le thread parent ne peut pas être obtenue dans le thread enfant. Y a-t-il donc un moyen d'amener le thread enfant pour accéder à la valeur dans le thread parent? L'héritage de réadlation a vu le jour à l'avenir. Héritage en héritage de la modification de ThreadLocal et fournit une fonctionnalité dans laquelle les enfants peuvent accéder aux variables locales définies dans le thread parent.
Tout d'abord, allons au code source de la classe HéritableThreadlocal à lire, comme suit:
La classe publique héritage HéritageThreadLocal <T> étend ThreadLocal <T> {public hheRitableThreAlLocal () {} // (1) Protected t ChildValue (t var1) {return var1; } // (2) ThreadLocalmap getmap (Thread var1) {return var1.inheritableThreadLocals; } // (3) void CreateMap (Thread var1, t var2) {var1.inheritableThreadLocals = new ThreadLocalmap (this, var2); }}Vous pouvez voir que l'héritage de la lecture hérite de trois méthodes threadlocal et réécrites. Le code ci-dessus a été marqué. Code (3) On peut voir que HériteraTHREADLOCAL réécrit la méthode CreateMAP, il peut donc être vu que lorsque la méthode définie est appelée pour la première fois, l'instance de la variable Héréativement en cas de lecture du thread actuel est créée, plutôt que des threadlocals.
CODE (2) Vous pouvez savoir que lorsque la méthode GET est appelée pour obtenir la variable de carte interne du thread actuel, le héritage est obtenu, et non ThreadLocals.
Le point clé est ici, lorsque le code réécrit (1) est exécuté et comment implémenter que le thread enfant peut accéder aux variables locales du thread parent. En commençant par le code créé par Thread, le constructeur par défaut du thread et le constructeur de la classe thread.java sont les suivants:
/ ** * Créé par Cong le 2018/6/3. * / thread public (Runnable Target) {init (null, cible, "Thread-" + nextThreadnum (), 0); } private void init (ThreadGroup G, Runnable Target, String Name, Long StackSize, AccessControlContext ACC) {// ... // (4) Obtenez le thread actuel Parent = CurrentThread (); // ... // (5) Si la variable héritée de la réadlation du thread parent n'est pas nul si (parent.inheritableThreadLocals! = Null) // (6) Définit la variable héritée de la lecture dans le thread enfant. this.stackSize = stackSize; tid = nextThreaDID (); }Lors de la création d'un thread, la méthode init sera appelée dans le constructeur. Comme mentionné précédemment, la classe HéritableThreadLocal Get et la méthode SET exploite la variable héritable de la lecture, de sorte que la variable héritée de la réadlocale ici n'est pas nul, de sorte que le code (6) sera exécuté. Voyons le code source de la méthode CreateIhheritedMap, comme suit:
statique threadLocalmap createIhheritedMap (threadLocalmap parentMap) {return new ThreadLocalmap (parentMap); }Vous pouvez voir que Create IheritedMap utilise la variable Héréateur de réadaptation du thread parent comme constructeur pour créer une nouvelle variable threadLocalmap, puis l'a attribuée à la variable HéréditableThreadLocals du thread enfant. Entrez ensuite le constructeur du threadlocalmap. Le code source est le suivant:
TikeLocalmap privé (threadLocalmap parentMap) {entrée [] parentTable = parentMap.Table; int len = parentTable.length; SetThreshold (len); table = nouvelle entrée [len]; pour (int j = 0; j <len; j ++) {entrée e = parentTable [j]; if (e! = null) {@SuppressWarnings ("Unchecked") ThreadLocal <objet> key = (ThreadLocal <Bject>) e.get (); if (key! = null) {// (7) Appelez la méthode remplacée Valeur de l'objet = key.childValue (e.Value); // return e.Value Entry c = new Entry (key, valeur); int h = key.threadLocalHashCode & (len - 1); while (table [h]! = null) h = nextIndex (h, len); Tableau [H] = C; taille ++; }}}}}}Ce que le code ci-dessus fait est de copier la valeur de la variable membre du thread de parent HéritALTHREADLOCALS vers le nouvel objet ThreadLocalmap, et le code (1) réécrit par le code (7) Héréitable de la classe Readlocal est également en vue.
En général: la classe HéritableThreadLocal réécrit le code (2) et (3) et enregistre les variables locales dans la variable héritable de réadroduction du thread spécifique. Lorsque le thread définit la variable via la méthode SET ou GET de l'instance héritée de la modification, elle créera la variable Héréation de ReadLocals du thread actuel. Lorsque le thread parent crée le fil d'enfant,
Le constructeur copiera la variable locale dans la variable HéritableThreadLocals dans le thread parent et le copie dans la variable HéritableThreadLocals du thread enfant.
Une fois que le principe est bien compris, prenons un exemple pour vérifier ce que nous savons ci-dessus, comme suit:
Package com.hjc; / ** * créé par Cong le 2018/6/3. * / public class hheRitableThreadLocalTest {// (1) Créer un thread variable public static threadLocal <string> threadLocal = new ThreadLocal <string> (); public static void main (String [] args) {// (2) définir la variable de thread ThreadLocal.set ("Hello Java"); // (3) Démarrer Thread Thread Thread Thread = new (new Runnable () {public void run () {// (4) La valeur du thread de l'enfant sort la variable de thread System.out.println ("Subthread:" + ThreadLocal.get ());}}); thread.start (); // (5) Le thread principal sortira le System Valeur de la variable de thread.out.println ("Thread Parent:" + ThreadLocal.get ()); }}Les résultats de l'opération sont les suivants:
C'est-à-dire que, une fois que la même variable threadlocale est définie dans le thread parent, il ne peut pas être obtenu dans le thread enfant. Selon l'introduction dans la section précédente, cela devrait être un phénomène normal, car le thread actuel est un thread enfant lorsque le thread enfant appelle la méthode GET, et la méthode de définition est appelée pour définir la variable de thread est le thread principal. Les deux sont des threads différents, et naturellement le thread enfant renvoie NULL lors de l'accès.
Y a-t-il donc un moyen d'amener le thread enfant pour accéder à la valeur dans le thread parent? La réponse est oui, alors utilisez l'héritage
Modifiez le code (1) de l'exemple ci-dessus pour:
// (1) Créer du thread variable public static threadLocal <string> threadLocal = new hheRitableThreadLocal <string> ();
Les résultats de l'opération sont les suivants:
On peut voir que la valeur de la variable de thread peut désormais être obtenue normalement à partir du thread enfant. Donc, dans quelles circonstances les enfants ont-ils besoin d'obtenir des variables threadlocales à partir du thread parent?
Il existe de nombreuses situations, telles que la variable threadlocal qui stocke les informations de connexion des utilisateurs. Il est très probable que les sous-efforts doivent également utiliser les informations de connexion des utilisateurs. Par exemple, certains middleware doivent utiliser un ID de suivi unifié pour enregistrer l'intégralité du lien d'appel.
Utilisation du threadlocal dans la portée de la demande de requête printemps
Nous savons que lors de la configuration d'un bean en XML à Spring, vous pouvez spécifier l'attribut de portée pour configurer la portée du bean pour être singleton, prototype, demande, session, etc. Le principe d'implémentation de la portée du bean est implémenté à l'aide de threadlocal. Si vous voulez qu'un haricot dans votre conteneur à ressort ait une portée du Web,
En plus des attributs de portée correspondants nécessaires pour configurer le niveau de bean, il doit également être configuré dans web.xml comme suit:
<Douger> <écouteur-classe> org.springframework.web.context.request.requestContextListener </ auteur-classe> </diner>
Ici, nous examinons principalement deux méthodes de requestContextListener:
Public vide DemandeInitialized (ServletRequestEvent DemandeEvent)
et
PUBLIC VOID RECHDERDESTROYED (ServLetRequestEvent DemandeEvent)
Lorsqu'une demande Web arrive, la méthode de demande inditifiée sera exécutée:
public void requestInitialized (ServLetRequestEvent requevantEvent) {..... omit httpservletRequest request = (httpservletRequest) requestEvent.getServletRequest (); ServLetRequestAttributes Attributes = new ServLetRequestAttributes (demande); request.setAttribute (request_attributes_attribute, attributs); LocalEcontexTholder.setLocale (request.getLocale ()); // Définissez l'attribut sur la variable threadLocal requestContexTholder.SetRequestAttributes (attributs); } public static void setRequestAttributes (requestAttributes Attributs) {setRequestAttributes (attributs, false); } public static void setRequestAttributes (requestAttributes Attributes, booléen héritable) {if (attributs == null) {reseTrequestAttributes (); } else {// Default héritable = false if (héritable) {héritagereAquestAtTributSholder.set (attributs); requestAtTrributeSholder.Remove (); } else {requestAtTributSholder.set (attributs); héritage de relecture deattributeholder.remove (); }}}Vous pouvez voir le code source ci-dessus. Étant donné que le héritable par défaut est faux, nos valeurs d'attribut sont placées dans requestAtTrributeShoder et sa définition est:
Final Final Static privé <quequetAttributes> requestAttributeSholder = new NamedThreadLocal <QouandAttributes> ("Attributs de demande"); Final Final Static privé <quequetAttributes> Héritage deHitABeStAtTrributeSholder = new NABLEATHERITABLETHREADLOCAL <DEDGETATTRIBUTES> ("Context de la demande");Parmi eux, NamedThreadLocal <T> étend Threadlocal <T>, il n'est donc pas héritable.
Parmi eux, NamedThreadLocal <T> étend Threadlocal <T>, il n'est donc pas héritable.
NameInHeritableThreadLocal <T> étend HéritertHreadLocal <T>, il a donc l'héritage, de sorte que la valeur d'attribut placée dans le plan de demande de demande par défaut ne peut pas être obtenue dans le thread enfant.
Lorsque la demande se termine, la méthode de requestdestroyed est appelée et le code source est le suivant:
public void requestDestRoyed (servLetRequestEvent requevantEvent) {servLetRequestAttributes Attributes = (servLetRequestAttributes) requestEvent.getServletRequest (). getAttribute (request_attributes_attribut); ServLetRequestAttributes ThreadAttributes = (servLetRequestAtTributes) requestContexTholder.getRequestAttributes (); if (ThreadAttributes! = null) {// Nous sommes très susceptibles d'effacer le thread du thread actuel dans le thread de demande initial if (attributs == null) {attributes = threadAttributes; } // Lorsque la demande se termine, effacez la variable de thread du thread actuel. LocalEcontexTholder.resetLocaleContext (); RequestContexTholder.ResetRequestAttributes (); } if (attributes! = null) {attributs.requestCompleted (); }}Ensuite, jetons un coup d'œil à la logique de l'appel de demande Web du tableau de synchronisation:
C'est-à-dire que chaque fois qu'une demande Web est lancée avant de traiter le contexte (application spécifique) dans Tomcat, la propriété de demande ContexTholder sera définie après les correspondances de l'hôte, afin que le soldatholder de demande n'est pas vide et sera effacé à la fin de la demande.
Par conséquent, par défaut, les threads d'enfants d'attribut placés dans le requête ContexTholder ne peuvent pas être accessibles et les haricots dans la portée de la demande de Spring sont implémentés à l'aide de ThreadLocal.
Ensuite, un exemple de demande de simulation est effectué, le code est le suivant:
La configuration Web.xml est la suivante:
Parce qu'il s'agit d'une portée de demande, il doit s'agir d'un projet Web et le requestContextListener doit être configuré sur web.xml.
<Douger> <écouteur-classe> org.springframework.web.context.request.requestContextListener </ auteur-classe> </diner>
Injectez ensuite un haricot de portée de demande dans le conteneur IOC. Le code est le suivant:
<bean id = "requestBean" scope = "request"> <propriété name = "name" value = "hjc" /> <aop: scoped-proxy /> </ bean>
Le code de test est le suivant:
@Webresource ("/ testService") classe publique testrpc {@autowired private requestBean requestInfo; @ResourceMapping ("Test") Public ActionResult Test (context de l'erreur Contexte) {ActionResult result = new ActionResult (); pvginfo.setName ("hjc"); String name = requestInfo.getName (); result.setValue (nom); Résultat de retour; }}Comme mentionné ci-dessus, configurez d'abord le requestContextListener dans web.xml, puis injectez l'instance de demande dans le conteneur IOC avec la portée de la demande. Enfin, l'instance de demande est injectée dans TestRPC. Méthode Test appelle d'abord la méthode requestInfo setName pour définir l'attribut de nom, puis obtenez l'attribut de nom et renvoyez.
Ici, si l'objet requestInfo est singleton, après que plusieurs threads appellent la méthode de test en même temps, chaque thread est une opération SET-GET. Cette opération n'est pas atomique et entraînera des problèmes de sécurité des filetages. La portée déclarée ici est le niveau de demande, et chaque thread a une variable locale avec requestInfo.
Le tableau de synchronisation de l'exemple de demande de méthode ci-dessus est le suivant:
Nous devons nous concentrer sur ce qui se passe lors du test d'appel:
En fait, la requestInfo créée plus tôt est après avoir été procassée par CGLIB (si vous êtes intéressé, vous pouvez étudier ScopedProxyFactoryBean et d'autres types), donc lorsque vous appelez SetName ou GetName, vous serez intercepté par DynamicadVissedInterceptor. L'Interceptor appellera éventuellement la méthode GET de requierScope pour obtenir les variables locales maintenues par le thread actuel.
La clé est ici. Nous devons examiner le code source de la méthode de requêtes GET comme suit:
Objet public get (String Name, objectFactory objectFactory) {requestAttributes attributes = requestContexTholder.currentRequestAttributes (); // (1) objet scopedObject = attributes.getAttribute (name, getScope ()); if (scopedObject == null) {scopedObject = objectFactory.getObject (); // (2) attributs.setAttribute (name, scopedObject, getScope ()); // (3)} return ScopedObject; }On peut voir que lorsqu'une demande est lancée, le requestAtTrributeSholder sera défini en appelant requestContextListener.Requestinitialized dans requestContextListener.SetRequestAttribute.
Ensuite, une fois que la demande est acheminée vers la méthode de test de TestRPC, la première fois que la méthode SetName est appelée dans la méthode de test, la méthode requestscope.get () sera finalement appelée. Le code de la méthode GET (1) obtient la valeur de l'ensemble d'attributs enregistré par la variable thread-locale requestAtTrributeSholder définie via requestContextListener.requestinitialialized.
Vérifiez ensuite s'il y a un attribut nommé demandesInfo dans l'ensemble d'attributs. Puisqu'il s'agit du premier appel, il n'existe pas, donc le code sera exécuté (2) et laissera Spring créer un objet de demande, puis le définira sur les attributs d'attribut, c'est-à-dire qu'il sera enregistré dans la mémoire locale du thread de demande actuel. Retournez ensuite l'objet créé et appelez le nom SetNon de l'objet créé.
Enfin, la méthode GetName est appelée dans la méthode de test et la méthode requestscope.get () sera appelée. Le code de la méthode GET (1) obtient la variable locale de filetage requestAttributes définie via requestContextListener.requestinitialialized, puis voyez s'il existe un attribut nommé demandesInfo dans l'ensemble d'attribut.
Étant donné que le bean nommé demandeInfo a été défini sur la variable ThreadLocal lorsqu'il est appelé pour SetName pour la première fois, et que le même thread est appelé pour setName et getName, l'objet de demande créé lorsque l'appel setName est directement renvoyé ici, puis sa méthode GetName est appelée.
Jusqu'à présent, nous avons compris le principe de mise en œuvre du threadlocal et souligné que le threadlocal ne prend pas en charge l'héritage; Ensuite, nous avons immédiatement expliqué comment l'héritage de la rémunération compense la fonctionnalité que ThreadLocal ne prend pas en charge l'héritage; Enfin, nous avons brièvement introduit comment utiliser ThreadLocal dans le framework Spring pour implémenter la portée du bean de la demande.
Résumer
Ce qui précède est l'intégralité du contenu de cet article. J'espère que le contenu de cet article a une certaine valeur de référence pour l'étude ou le travail de chacun. Si vous avez des questions, vous pouvez laisser un message pour communiquer. Merci pour votre soutien à wulin.com.