Récemment, j'ai lu le code de couche Android Framework et vu la classe ThreadLocal. J'étais un peu inconnu, alors j'ai lu divers blogs connexes un par un. J'ai ensuite étudié le code source et constaté que ma compréhension était différente des articles de blog que j'ai lu auparavant, j'ai donc décidé d'écrire un article pour parler de ma compréhension, en espérant qu'il puisse jouer le rôle suivant:
- peut effacer les résultats de la recherche et approfondir votre compréhension;
- Il peut jouer un rôle dans l'attraction de Jade et d'aider les étudiants intéressés à effacer leurs idées;
- Partagez votre expérience d'apprentissage et communiquez et apprenez avec tout le monde.
1. Qu'est-ce que Threadlocal
ThreadLocal est la classe de base de la bibliothèque de classe Java, sous le package java.lang;
L'explication officielle est la suivante:
Implémente un stockage de thread-local, c'est-à-dire une variable pour laquelle chaque thread a sa propre valeur. Tous les threads partagent le même objet threadlocal, mais chacun voit une valeur différente lors de l'accès, et les modifications apportées par un thread n'affectent pas les autres threads. L'implémentation prend en charge les valeurs nuls.
Le sens général est:
Le mécanisme de stockage local des threads peut être mis en œuvre. La variable threadlocal est une variable qui peut avoir des valeurs différentes dans différents threads. Tous les threads peuvent partager le même objet ThreadLocal, mais différents threads peuvent obtenir des valeurs différentes une fois accédés, et les modifications de tout thread ne affecteront pas d'autres threads. Les implémentations de classe prennent en charge les valeurs nul (les valeurs nulles peuvent être passées et accédés dans les méthodes SET et GET).
En résumé, il y a trois caractéristiques:
- Obtenez des valeurs différentes lorsqu'elles sont accessibles par différents threads
- Tout fil de thread n'affectera pas d'autres threads
- Soutenir Null
Voici des exemples de ces fonctionnalités. Tout d'abord, nous définissons une classe de test. Dans cette classe, nous vérifions les trois fonctionnalités mentionnées ci-dessus. La définition de la classe est la suivante:
Test.java
Classe publique Test {// Définir le nom de threadlocal statique privé ThreadLocal Private; public static void main (String [] args) lève une exception {name = new ThreadLocal (); // Define Thread Athread a = new Thread () {public void run () {System.out.println ("Avant Invoke Set, Value est:" + name.get ()); Name.Set (" A "); System.out.println (" After Invoke Set, la valeur est: "+ name.get ());}}; // définir le thread bthread b = new Thread () {public void run () {System.out.println (" Avant Invoke Set, la valeur est: "+ name.get ()); Name.set (" thread b "); System.out.out.Println (" After Invoke. : "+ name.get ());}}; // non invoquer l'ensemble, imprimer la valeur est nullSystem.out.println (name.get ()); // invoquer le jeu pour remplir un valiename.set (" Thread Main "); // Démarrer le thread aa.start (); a.join (); // imprime la valeur après le thread par thread Asystem.out.out.Println (Name.get);) Bb.start (); b.join (); // imprime la valeur après avoir modifié la valeur par thread bsystem.out.println (name.get ())}}Analyse du code:
Dans la définition, nous pouvons voir qu'un seul objet threadlocal est déclaré, et les trois autres threads (thread principal, thread A et Thread B) partagent le même objet; Ensuite, la valeur de l'objet est modifiée dans différents threads et la valeur de l'objet est accessible dans différents threads, et le résultat est sorti à afficher dans la console.
Voir les résultats:
À partir des résultats de sortie de la console, vous pouvez voir qu'il y a trois sorties nulles. En effet, l'objet n'a pas été attribué avant la sortie, ce qui a vérifié la fonction de support NULL. De plus, on peut constater que dans chaque thread, j'ai modifié la valeur de l'objet, mais lorsque d'autres threads accèdent à l'objet, ce n'est pas la valeur modifiée, mais la valeur de thread-local; Cela vérifie également les deux autres fonctionnalités.
2. Le rôle du filetage
Tout le monde sait que ses scénarios d'utilisation sont principalement des programmes multi-thread. Quant à sa fonction spécifique, comment dites-vous cela? Je pense que cela ne peut être défini que de manière générale, car les attributs fonctionnels d'une chose limiteront la pensée de chacun après sa définition. Par exemple, les couteaux de cuisine sont utilisés pour couper les légumes, et de nombreuses personnes ne l'utiliseront pas pour couper les pastèques.
Ici, je parlerai de ma compréhension de sa fonction pour référence uniquement et j'espère que cela sera utile. Décrivons-le de cette façon. Lorsqu'un programme multithread doit encapsuler certaines tâches de la plupart des threads (c'est-à-dire un code dans la méthode d'exécution), ThreadLocal peut être utilisé pour envelopper les variables de membres liées au thread dans le corps d'encapsulation pour assurer l'exclusivité de l'accès au fil, et tous les threads peuvent partager un objet d'encapsulation; Vous pouvez vous référer à Looper dans Android. Les programmeurs qui ne peuvent pas décrire les problèmes de code ne sont pas de bons programmeurs;
Regardez le code: un outil pour énoncer le code long d'un thread (créé pour illustrer le problème)
Statisticcosttime.java
// classe cette statistique le coût timePublic class statisticcosttime {// enregistrez le startTime // private threadLocal startTime = new ThreadLocal (); private long startime; // private threadlocal costtime = new ThreadLocal (); private long Costtime; private statisticsTime () {} // singletonpublic static final statisticCosttime ShareSance () {retour InstanceFactory.instance;} classe statique privée instanceFactory {private static final statisticCosttime instance = new StatisticCosttime ();} // startPublic void start () {// starttime.set (System. NanoTime ()); starttime = System.NanoTime ();} // endpublic end () {// Costtime.Set (System. startTime.get ()); CostTime = System.NanoTime () - StartTime;} public long getStarttime () {return startTime; // return startTime.get ();} public long getCosttime () {// return CostTime.get (); return Costtime;}D'accord, la conception de l'outil est terminée, maintenant nous l'utilisons pour compter les fils longs et essayez:
Main.java
classe publique main {public static void main (string [] args) lève l'exception {// définir le thread Athread a = new Thread () {public void run () {try {// start enregistre timestatisticCostTime.shareInSitance (). start (); sleep (200); // imprimez l'heure de début d'une System.out.println ("a-starttime:" + statisticcosttime.shareinstance (). GetStartTime ()); // terminer le recordstatisticCostTime.shareInstance (). End (); // imprime le coût d'un système.out.println ("a:" + statisticstime.shareinSTance (). GetCosttime ();} CatchCostTime. e) {}}}; // start aa.start (); // définir le thread bthread b = new Thread () {public void run () {try {// enregistrer l'heure de début de B1StatisticCosttime.shareInstance (). start (); sleep (100); // imprimez l'heure de début à ConsoleSystem.out.println ("B1-StartTime:" + StatisticCostTime.ShareInstance (). GetStTime ()); // End Record Start Heure de B1StatisticCostTime.ShareInstance (). B1System.out.println ("b1:" + statisticCostTime.shareInstance (). GetCostTime ()); // Démarrer l'heure d'enregistrement de B2StatisticCostTime.shareInstance (). Start (); sleep (100); // imprimer l'heure de début de l'heure de début de B2System.out.println ("b2-starttime:" + statisticCostTime.shareInstance (). GetStTime ()); // terminant l'heure d'enregistrement de B2StatisticCostTime.shareInstance (). End (); // imprimer le temps de coût du temps de coût du temps de coût du temps de coût du temps de coût du temps de coût du temps de coût du temps de coût du temps de coût du temps de coût du temps de coût du temps de coût du temps de coût du temps de coût de l'heure du coût de l'heure de la fin B2System.out.println ("b2:" + statisticCostTime.shareInstance (). GetCostTime ());} catch (exception e) {}}}; b.start ();}}Après avoir exécuté le code, le résultat de sortie est le suivant: la précision du résultat de sortie est des nanosecondes.
Cela dépend de savoir si le résultat est différent de ce à quoi nous nous attendions. J'ai trouvé que le résultat de A devrait être approximativement égal à B1 + B2. Comment se fait-il que cela devienne la même chose que B2? La réponse est que lorsque nous définissons les variables de démarrage et de temps de coût, l'intention d'origine ne doit pas être partagée, mais doit être exclusive au fil. Ici, les variables sont partagées avec le singleton, donc lors du calcul de la valeur de A, le starttime a en fait été modifié par B2, donc le même résultat que B2 est la sortie.
Ouvrez maintenant la partie commentée dans StatisticCostTime et essayons-le en le modifiant en méthode de déclaration de ThreadLocal.
Voir les résultats:
Ah! Cela a réalisé l'effet attendu. À l'heure actuelle, certains étudiants diraient que ce n'est pas un accès à la courrier par fil, et puis-je assurer la sécurité des filetages tant que j'utilise Threadlocal? La réponse est non! Tout d'abord, nous pouvons comprendre pourquoi il y a des problèmes de sécurité des fils, mais il n'y a que deux situations:
1. Vous avez partagé des ressources qui ne devraient pas être partagées entre les fils;
2. Vous ne garantissez pas l'accès ordonné aux ressources partagées entre les threads;
Le premier peut être résolu par la méthode "temps d'échange spatial", en utilisant ThreadLocal (vous pouvez également déclarer directement les variables locales de thread), et le second peut être résolu par la méthode "temps d'échange spatial", ce qui n'est évidemment pas ce que Threadlocal peut faire.
3. Principe fileux
Le principe de mise en œuvre est en fait très simple. Chaque fois que l'opération de lecture et d'écriture de l'objet ThreadLocal est en fait une opération de lecture et d'écriture dans l'objet Valeurs du thread; Ici, nous clarifions qu'il n'y a pas de création d'une copie de la variable, car l'espace mémoire alloué par la variable n'est pas utilisé pour stocker l'objet T, mais les valeurs du thread sont utilisées pour stocker l'objet T; Chaque fois que nous appelons la méthode SET ThreadLocal dans le thread, il s'agit en fait d'un processus d'écriture de l'objet dans l'objet Valeurs correspondant du thread; Lors de l'appel de la méthode GET ThreadLocal, il s'agit en fait d'un processus d'obtention de l'objet de l'objet Valeurs correspondant du thread.
Voir le code source:
Ensemble de variables de membre threadlocal
/ ** * Définit la valeur de cette variable pour le thread actuel. Si défini sur * {@code null}, la valeur sera définie sur null et l'entrée sous-jacente sera toujours présente. * * @param Valeur La nouvelle valeur de la variable pour le thread de l'appelant. * / public void set (t valeur) {thread currentThread = thread.currentThread (); Valeurs valeurs = valeurs (currentThread); if (valeurs == null) {valeurs = initializeValues (currentThread); } valeurs.put (cette valeur);}Méthode des membres de Treadlocal Get
/ ** * Renvoie la valeur de cette variable pour le thread actuel. Si une entrée * n'existe pas encore pour cette variable sur ce thread, cette méthode créera * une entrée, remplira la valeur avec le résultat de * {@Link #InitialValue ()}. * * @return la valeur actuelle de la variable pour le thread d'appel. * / @ Suppresswarnings ("non contrôlé") public t get () {// optimisé pour le chemin rapide. Thread currentthread = thread.currentThread (); Valeurs valeurs = valeurs (currentThread); if (valeurs! = null) {objet [] table = valeurs.Table; int index = hash & valeurs.mask; if (this.reference == tableau [index]) {return (t) Table [index + 1]; }} else {valeurs = initializeValues (currentThread); } return (t) valeurs.getafTermiss (this);}Méthode de membre threadlocal InitializeValues
/ ** * Crée une instance de valeurs pour ce thread et ce type de variable. * / Valeurs initializeValues (courant de thread) {return current.localValues = news ();}Valeurs de méthode des membres threadlocal
/ ** * Obtient l'instance de valeurs pour ce thread et ce type de variable. * / Valeurs valeurs (courant de thread) {return Current.LocalValues;}Alors, comment lisez-vous et écrivez-vous des objets dans ces valeurs?
Les valeurs existent en tant que classe interne de threadlocal; Ces valeurs comprennent un objet de tableau important [], qui est la partie clé de la réponse à la question. Il est utilisé pour stocker différents types de variables Treadlocal dans le fil. La question est donc de savoir comment vous assurez-vous de ne pas obtenir de valeurs d'autres types lorsque vous prenez des variables d'un certain type? En général, une carte sera cartographiée en fonction de la valeur clé; Oui, l'idée est cette idée, mais elle n'est pas implémentée avec MAP ici, c'est un mécanisme de carte implémenté avec un objet []; Cependant, si vous souhaitez utiliser MAP pour le comprendre, ce n'est pas possible car le mécanisme est le même; La clé correspond en fait à la référence faible du threadlocal, et la valeur correspond à l'objet dans lequel nous avons passé.
Expliquons comment utiliser l'objet [] pour implémenter le mécanisme de carte (reportez-vous à la figure 1); Il utilise la parité de l'indice du tableau pour distinguer la clé et la valeur, c'est-à-dire que le tableau ci-dessous stocke la clé à la position uniforme et le nombre impaire stocke la valeur. C'est ainsi que cela se fait. Si les étudiants intéressés souhaitent connaître la mise en œuvre de l'algorithme, ils peuvent l'étudier en profondeur, je ne l'expliquerai pas en détail ici.
Sur la base du premier exemple précédent, la situation de stockage est analysée:
Lorsque le programme est exécuté, il y a trois fils A, B et Main. Lorsque name.set () est appelé dans le thread, trois espaces de mémoire identiques sont alloués dans la zone du tas pour trois instances de thread en même temps pour stocker l'objet VALEUR, en utilisant la référence de nom comme clé, et l'objet spécifique est stocké comme valeur dans trois objets différents [] (voir la figure ci-dessous):
4. Résumé
ThreadLocal ne peut pas résoudre complètement le problème de concurrence lors de la programmation multi-thread. Ce problème nécessite également des solutions différentes à choisir selon les différentes situations, "l'espace pour le temps" ou le "temps pour l'espace".
La plus grande fonction du threadlocal est de convertir les variables partagées en filetage en variables de thread-locales pour obtenir l'isolement entre les threads.
Ce qui précède consiste à comprendre rapidement le threadlocal en Java. J'espère que ce sera utile à tout le monde. S'il y a des lacunes, veuillez laisser un message pour le signaler. Merci vos amis pour votre soutien à ce site.