Il y a toujours une différence entre les variables de l'élément threadlocal et du filetage. La classe ThreadLocal fournit des variables locales de thread. Cette variable locale est différente des variables des membres généraux. Lorsque la variable ThreadLocal est utilisée par plusieurs threads, chaque thread ne peut obtenir qu'une seule copie de la variable. Ceci est une description de l'API Java. En lisant le code source de l'API, j'ai constaté qu'il ne s'agissait pas d'une copie. Quel est le concept d'une copie? Clones? Ou autre chose, trop vague.
Pour être précis, le registre (map <thread, t>) à l'intérieur de la variable de type threadlocal a changé, mais la variable de type threadlocal lui-même est en effet une, et c'est l'essence!
Voici un exemple:
1. Exemples standard
La classe MyThreadLocal est définie, et son objet TLT est créé, et il est utilisé par quatre threads. En conséquence, les variables TLT des quatre threads ne partagent pas. La seconde consiste à utiliser la leur, ce qui montre que les quatre threads utilisent une copie de TLT (clone).
/ ** * Utilisez ThreadLocal Class * / public class MyThreadLocal {// Définissez une variable ThreadLocal pour enregistrer INT ou INTEGER DATA PRIVET ThreadLocal <Integer> tl = new ThreadLocal <Neger> () {@Override Protected Integer initialValue () {return 0; }}; Public Integer getNextNum () {// Obtenez la valeur de TL et ajoutez 1, et mettez à jour la valeur de T1 tl.set (tl.get () + 1); return tl.get (); }} / ** * Test Thread * / public class TestThread étend Thread {private myThreadlocal tlt = new MyThreadLocal (); Public TestThread (myThreadLocal Tlt) {this.tlt = tlt; } @Override public void run () {for (int i = 0; i <3; i ++) {System.out.println (thread.currentThread (). GetName () + "/ t" + tlt.getNextNum ()); }}} / ** * Test ThreadLocal * / Public Class Test {public static void main (String [] args) {myThreadLocal tlt = new MyThreadLocal (); Thread t1 = new TestThread (tlt); Thread t2 = new TestThread (tlt); Thread t3 = new TestThread (tlt); Thread t4 = new TestThread (tlt); t1.start (); t2.start (); t3.start (); T4.Start (); }}
On peut voir que les trois threads sont numérotés indépendamment et ne se affectent pas mutuellement:
Thread-0 1 Thread-1 1 Thread-0 2 Thread-1 2 Thread-0 3 Thread-1 3 Thread-2 1 Thread-3 1 Thread-2 2 Thread-3 2 Thread-2 3 Thread-3 3 Processus terminé avec le code de sortie 0
L'objet TLT en est un, et l'objet TL absurde en est également un, car la relation combinée est un à un. Cependant, à mesure que le nombre de threads augmente, de nombreux objets entiers seront créés. C'est juste que l'entier et l'int sont déjà courants. Je ne sens donc pas les propriétés d'objet de l'entier.
2. N'utilisez pas de threadlocal
Si vous n'utilisez pas ThreadLocal, il vous suffit de redéfinir la classe MyThreadLocal en tant que:
/ ** * Utilisez ThreadLocal Class * / public class MyThreadLocal {private Integer t1 = 0; entier public getNextNum () {return t1 = t1 + 1; } // Définissez une variable threadLocal pour enregistrer des données int ou entier // threadLocal <nteger> tl = new ThreadLocal <Integer> () {// @Override // Protected Integer initialValue () {// return 0; //} //}; // // public Integer getNextNum () {// // Obtenez la valeur de TL et ajoutez 1, et mettez à jour la valeur de T1 // tl.set (tl.get () + 1); // return tl.get (); //}}
Ensuite, exécutez le test:
Thread-2 1 Thread-2 2 Thread-1 4 Thread-1 6 Thread-3 3 Thread-3 9 Thread-3 10 Thread-1 8 Thread-0 7 Thread-0 11 Thread-0 12 Thread-2 5 Processus terminé avec le code de sortie 0
De là, nous pouvons voir que les quatre threads partagent la variable TLT et chaque thread modifie directement les propriétés du TLT.
3. Réalisez le threadlocal par vous-même
package com.lavasoft.test2; Importer java.util.collections; import java.util.hashmap; importation java.util.map; / ** * Utilisez ThreadLocal Class * / public class MyThreadLocal {// Définissez une variable threadlocal pour enregistrer INT ou INTEGER DATA PRIVATE com.lavasoft.test2.ThreadLocal <Integer> TL = new Com.lavasoft.Test2 }}; Public Integer getNextNum () {// Obtenez la valeur de TL et ajoutez 1, et mettez à jour la valeur de T1 tl.set (tl.get () + 1); return tl.get (); }} classe threadLocal <T> {Private Map <Thread, t> map = collections.SynchronizedMap (new HashMap <Thread, t> ()); public threadLocal () {} protégé t initialValue () {return null; } public t get () {thread t = thread.currentThread (); T obj = map.get (t); if (obj == null &&! map.containsKey (t)) {obj = initialValue (); map.put (t, obj); } return obj; } public void set (t valeur) {map.put (thread.currentThread (), valeur); } public void retire () {map.remove (thread.currentThread ()); }}
Exécutez le test:
Thread-0 1 Thread-0 2 Thread-0 3 Thread-2 1 Thread-2 2 2 Thread-3 1 Thread-2 3 Thread-3 2 Thread-1 1 Thread-3 3 Thread-1 Thread-1-1 3 Processus terminé avec le code de sortie 0
Étonnamment, cette version Copycat de ThreadLocal fonctionne également bien, implémentant la fonction de ThreadLocal dans l'API Java.
4. Voir l'essence à travers des phénomènes
En fait, du point de vue du programme, la variable TLT est en effet une, sans aucun doute. Mais pourquoi les chiffres imprimés ne se affectent-ils pas?
Est-ce à cause de l'utilisation entier? -----Non.
La raison en est: protégé t initialValue () et get (), car lorsque chaque appel de thread obtiendra (), il le créera s'il n'existe pas dans la carte. Lorsque cela s'appelle, une nouvelle variable est créée avec le type T. Chaque fois qu'ils sont nouvellement créés, bien sûr, chacun les utilise sans aucun effet les uns sur les autres.
Afin de voir clairement l'essence, remplacer entier et réécrire certaines classes:
package com.lavasoft.test2; Importer java.util.collections; import java.util.hashmap; importation java.util.map; / ** * Utilisez la classe threadlocal * / classe publique MyThreadLocal {// Définissez une variable threadlocal pour enregistrer des données int ou entier // FiledLocal <Ean> tl = new ThreadLocal <Ean> () {private com.lavasoft.test.ThreadLocal <Epan> tl = New com.lavasoft.Test2 initialValue () {return new Bean (); }}; @Override public String toString () {return "myThreadLocal {" + "tl =" + tl + '}'; } public bean getbean () {return tl.get (); }} classe threadLocal <T> {Private Map <Thread, t> map = collections.SynchronizedMap (new HashMap <Thread, t> ()); public threadLocal () {} protégé t initialValue () {return null; } public t get () {thread t = thread.currentThread (); T obj = map.get (t); if (obj == null &&! map.containsKey (t)) {obj = initialValue (); map.put (t, obj); } return obj; } public void set (t valeur) {map.put (thread.currentThread (), valeur); } public void retire () {map.remove (thread.currentThread ()); }} package com.lavasoft.test2; / ** * Tester Bean * / public class bean {private String id = "0"; name de chaîne privée = "Aucun"; public bean () {} public bean (chaîne id, nom de chaîne) {this.id = id; this.name = name; } public String getID () {return id; } public void setid (String id) {this.id = id; } public String getName () {Nom de retour; } public void setName (string name) {this.name = name; } public String showInfo () {return "bean {" + "id = '" + id +' / '' + ", name = '" + name +' / '' + '}'; }} package com.lavasoft.test2; / ** * Test Thread * / public class TestThread étend Thread {private myThreadlocal tlt = new MyThreadLocal (); Public TestThread (myThreadLocal Tlt) {this.tlt = tlt; } @Override public void run () {System.out.println (">>>>:" + tlt); pour (int i = 0; i <3; i ++) {System.out.println (thread.currentThread (). getName () + "/ t" + tlt.getBean () + "/ t" + tlt.getBean (). ShowInfo ()); }}}
Ensuite, exécutez le test:
>>>>>: MyThreadLocal{tl=com.lavasoft.test2.mythreadlocal$1@1de3f2d} >>>>>: myThreadlocal{tl=com.lavasoft.test2.mythreadlocal$1@1de3f2d} >>>>>:: myThreadLocal{tl=com.lavasoft.test2.bean@291aff bean {id = '0', name = 'nother'} thread-2 com.lavasoft.test2.bean@fe64b9 bean {id = '0', name = 'Aucun'} thread-3 com.lavasoft.test2.bean@186db54 bean {0 '0' 0 '0', 0 '0' 0 ', 0' 0 ', 0' 0 ', 0' 0 ', 0' 0 '0', 0 '0', 0 '0', 0 ', 0' 0 ', 0' 0 ', 0' 0 ', 0' 0 ', 0' 0 ', 0'. name = 'None'} thread-2 com.lavasoft.test2.bean@fe64b9 bean {id = '0', name = 'nothere'} thread-2 com.lavasoft.test2.bean@fe64b9 bean {id = '0', name = 'nul'} thread-2 com.lavasoft.test2.bean-0 com.lavasoft.test2.bean@291aff bean {id = '0', name = 'non'} thread-3 com.lavasoft.test2.bean@186db54 bean {id = '0', name = 'non'} thread-3 com.lavasoft.test2.Bean@186db54 bean {id = '0', name = 'not com.lavasoft.test2.bean@291aff bean {id = '0', name = 'non'} thread-0 com.lavasoft.test2.bean@291aff bean {id = '0', name = 'nue'} thread-0 com.lavasoft.test2.bean@291aff Bean {id = '0', nom = 'nul'} thread-1 com.lavasoft.test2.bean@291aff bean {id = '0', name = 'non'} Processus terminé avec le code de sortie 0
Il ressort clairement des résultats de l'impression que l'objet TLT de MyThreadlocal est en effet un, et l'objet TL de Threadlocal dans l'objet TLT en est également un. Cependant, lorsque T1T est utilisé pour chaque thread, le thread recréera l'objet bean et l'ajoutera à la carte threadlocal pour une utilisation.
Plusieurs malentendus sur le threadlocal:
1. ThreadLocal est une implémentation de threads Java
ThreadLocal est en effet lié aux threads Java, mais ce n'est pas une implémentation de threads Java, il est simplement utilisé pour maintenir les variables locales. Pour chaque thread, il fournit sa propre version variable, principalement pour éviter les conflits de thread, et chaque fil conserve sa propre version. Être indépendant les uns des autres et la modification ne se affectera pas mutuellement.
2. ThreadLocal est relatif à chaque session
ThreadLocal, comme son nom l'indique, s'adresse aux threads. Dans la programmation Web Java, chaque utilisateur a son propre identifiant de session du début à la fin de la session. Mais ThreadLocal n'est pas sur le calque de session. En fait, ThreadLocal est indépendant de la session utilisateur. Il s'agit d'un comportement côté serveur. Chaque fois qu'un serveur génère un nouveau thread, il maintient son propre threadlocal.
En ce qui concerne ce malentendu, je crois personnellement que cela devrait être le résultat du test local du développeur sur la base de certains serveurs d'applications. Comme nous le savons tous, les serveurs d'applications généraux conservent un ensemble de pools de threads, c'est-à-dire, pour chaque accès, un nouveau thread ne génère pas nécessairement. Au lieu de cela, j'ai une piscine de cache de fil. Pour accéder, trouvez d'abord les threads existants de la pool de cache. S'ils ont tout utilisé, un nouveau fil sera généré.
Par conséquent, comme le développeur est généralement le seul à se tester, le fardeau du serveur est très petit, ce qui conduit au partage du même fil à chaque fois que l'accès est, entraînant le malentendu: chaque session a un filetage
3. ThreadLocal est relatif à chaque thread. Chaque fois que l'utilisateur accède, il y aura un nouveau threadlocal.
Théoriquement, le threadlocal est en effet par rapport à chaque thread, chaque thread aura son propre threadlocal. Mais comme mentionné ci-dessus, les serveurs d'application généraux conservent un ensemble de pools de threads. Par conséquent, différents utilisateurs peuvent recevoir le même thread. Par conséquent, lorsque vous effectuez la tête de tête, vous devez faire attention à éviter le cache des variables threadlocal, ce qui a fait accéder à d'autres threads les variables de thread
4. Pour chaque accès utilisateur, ThreadLocal peut être utilisé plusieurs fois.
On peut dire que le threadlocal est une épée à double tranchant et peut avoir de très bons résultats s'ils sont utilisés. Cependant, si ThreadLocal n'est pas bien utilisé, ce sera le même que les variables globales. Le code ne peut pas être réutilisé et ne peut pas être testé indépendamment. Parce que certaines classes qui auraient pu être réutilisées reposent maintenant sur la variable threadlocal. S'il n'y a pas de threadlocal, ces classes deviennent indisponibles. Je pense personnellement que le threadlocal est bien utilisé et vaut la peine de se référer
1. Stockez l'utilisateur de session en cours: Quake Want Jet
2. Stockez certaines variables de contexte, telles que l'action de Webwork
3. Sessions de magasin, telles que les séances de printemps Hibernate Orm