Diagramme d'état du thread
Les fils comprennent les 5 états suivants.
1. Nouvel état: une fois l'objet de thread créé, il entre dans le nouvel état. Par exemple, thread thread = new Thread ().
2. État prêt (runnable): également appelé "état exécutable". Une fois l'objet thread créé, d'autres threads appellent la méthode start () de l'objet pour démarrer le thread. Par exemple, thread.start (). Un fil dans un état prêt peut être prévu pour s'exécuter par le CPU à tout moment.
3. État d'exécution (en cours d'exécution): Le thread obtient des autorisations CPU pour l'exécution. Il convient de noter que les threads ne peuvent entrer que l'état en cours d'exécution à partir de l'état prêt.
4. État bloqué: l'état bloqué signifie que le fil abandonne les droits d'utilisation du CPU pour une raison quelconque et cesse temporairement de fonctionner. Ce n'est que lorsque le fil entre dans l'état prêt qu'il a une chance d'aller à l'état de course. Il existe trois types de blocage:
(01) En attendant de bloquer - en appelant la méthode Wait () du thread, laissez le fil d'attente pour l'achèvement d'un certain travail.
(02) Blocage synchronisé - Un fil ne parvient pas à acquérir un verrou de synchronisation synchronisé (car le verrou est occupé par d'autres threads), il entrera dans un état de blocage synchronisé.
(03) Autre blocage - le fil entrera un état de blocage en appelant Sleep () ou join () du thread ou en émettant une demande d'E / S. Lorsque l'état de sommeil () a chronométré, join () a attendu que le fil se termine ou l'expression, ou le traitement d'E / S a été terminé, le fil est rentré à l'état prêt.
5. État mort: le thread a fini d'exécuter ou de quitter la méthode run () en raison d'une exception, et le fil met fin à son cycle de vie.
Implémentez le thread de méthodes multiples et runnable
Thread: Hériter de la classe de thread, implémentez la méthode d'exécution et appelez la méthode de démarrage dans la fonction principale pour démarrer le thread
Runnable: Interface, implémente l'interface runnable, la passe en tant que paramètre au constructeur de thread et appelle la méthode de démarrage en main
exemple:
La tâche de classe implémente Runnable {private int ticket = 10; @Override public void run () {for (int i = 0; i <20; i ++) {if (this.ticket> 0) {System.out.println (thread.currentThread (). GetName () + "Vendu Ticket" + this.ticket--); }}}}}}; classe publique RunNableTest {public static void main (String [] args) {tâche mytask = new task (); Thread t1 = nouveau thread (myTask); Thread t2 = nouveau thread (myTask); Thread t3 = nouveau thread (myTask); t1.start (); t2.start (); t3.start (); }} // threadtest.java CODE SOURCE CLASSE Mythread étend Thread {private int ticket = 10; public void run () {for (int i = 0; i <20; i ++) {if (this.ticket> 0) {System.out.println (this.getName () + "Ticket Sell: Ticket" + this.ticket--); }}}} public class threadtest {public static void main (String [] args) {// start 3 threads t1, t2, t3; Chaque fil vend 10 billets chacun! Mythread t1 = new mythread (); Mythread t2 = new mythread (); Mythread t3 = new mythread (); t1.start (); t2.start (); t3.start (); }};
La différence entre le thread et le runnable
Le thread est une classe et Runnable est une interface; Le thread lui-même est une classe qui implémente l'interface Runnable. Nous savons que "une classe ne peut avoir qu'une classe parent, mais elle peut implémenter plusieurs interfaces", donc Runnable a une meilleure évolutivité. De plus, Runnable peut également être utilisé pour le «partage des ressources». Autrement dit, plusieurs threads sont créés sur la base d'un certain objet Runnable, et ils partageront des ressources sur l'objet Runnable. Généralement, il est recommandé d'implémenter le multi-threading via "Runnable"!
L'exécution du thread et commencent
start (): sa fonction est de démarrer un nouveau thread et le nouveau thread exécutera la méthode Run () correspondante. start () ne peut pas être appelé à plusieurs reprises. start () démarre réellement le fil à travers la méthode locale start0 (). start0 () exécutera un nouveau thread et le nouveau thread appellera la méthode run ().
run (): run () peut être appelé à plusieurs reprises comme les méthodes de membres ordinaires. Si vous appelez Run () séparément, Run () sera exécuté dans le thread actuel et le nouveau fil ne sera pas démarré! Run () doit appeler directement la méthode run () du membre Runnable du thread et ne créera pas un nouveau thread.
// Demo.java Code source Classe MyThread étend Thread {public mythread (String name) {super (name); } public void run () {System.out.println (thread.currentThread (). getName () + "est en cours d'exécution"); }}; classe publique Demo {public static void main (String [] args) {thread mythread = new mythread ("mythread"); System.out.println (Thread.currentThread (). GetName () + "Appelez MyThread.Run ()"); mythread.run (); System.out.println (Thread.currentThread (). GetName () + "Appelez MyThread.Start ()"); mythread.start (); }}Sortir:
principal appel mythread.run () Main est RunningMain Call MyThread.start () Mythread est en cours d'exécution
synchronisé
En Java, chaque objet a un verrou de synchronisation. Lorsque nous appelons la méthode synchronisée de l'objet, le verrouillage de l'objet est acquis et synchronisé (OBJ) acquiert le verrouillage de synchronisation de l'objet "Obj". Un accès de threads différents au verrouillage de synchronisation s'exclut mutuellement. Le verrouillage de synchronisation de l'objet ne peut être acquis qu'à un seul thread à un certain moment. Grâce au verrou de synchronisation, nous pouvons obtenir un accès mutuellement exclusif à "l'objet / méthode" dans plusieurs threads. Par exemple, il y a maintenant deux threads A et Thread B, qui accéderont tous au "verrouillage synchronisé de l'objet OBJ". Supposons qu'à un moment donné, le thread A acquiert le "verrouillage de synchronisation de l'OBJ" et effectue certaines opérations; À l'heure actuelle, Thread B tente également d'acquérir le "verrouillage de synchronisation de l'OBJ" - Thread B ne parviendra pas à acquérir, il doit attendre que le thread A libère le "verrouillage de synchronisation d'Obj" et ne peut être exécuté que.
Règles de base
Article 1: Lorsqu'un thread accède à la "méthode synchronisée" ou "bloc de code synchronisé" de "un certain objet", d'autres threads seront bloqués de l'accès à la "méthode synchronisée" ou "bloc de code synchronisé" de "l'objet".
Article 2: Lorsqu'un thread accède à la "méthode synchronisée" ou au "bloc de code synchronisé" de "un certain objet", d'autres threads peuvent toujours accéder au bloc de code asynchronisé de "cet objet".
Article 3: Lorsqu'un thread accède à la "méthode synchronisée" ou au "bloc de code synchronisé" de "un certain objet", d'autres threads seront empêchés d'accéder à d'autres "méthodes synchronisées" ou "bloc de code synchronisé" de "l'objet".
Méthode synchronisée
public synchronisé void foo1 () {System.out.println ("Méthode synchronisée");} bloc de code synchronisé public void foo2 () {synchronisé (this) {System.out.println ("Méthode synchronisée"); }}Ceci dans le bloc de code synchronisé fait référence à l'objet actuel. Cela peut également être remplacé par d'autres objets, tels que celui-ci est remplacé par OBJ, puis foo2 () acquiert le verrou de synchronisation d'OBJ lorsqu'il est synchronisé (OBJ).
Les blocs de code synchronisés peuvent contrôler plus précisément les zones d'accès restreintes, et parfois effectuer plus efficacement
Verrouillage des instances et verrouillage global
Verrouillage de verrouillage d'instance sur un objet d'instance. Si la classe est un singleton, le verrou a également le concept de verrouillage mondial. Le mot-clé synchronisé correspond au verrouillage de l'instance.
Global Lock - Ce verrou est destiné à une classe. Peu importe le nombre d'objets l'instance, les threads partagent le verrou. Le verrou global correspond à statique synchronisée (ou verrouillée sur la classe ou l'objet Classloader de cette classe).
Classe pulbic quelque chose {public synchronisé void Issynca () {} public synchronisé void issyncb () {} public statique synchronisé void cSynca () {} public statique synchronisé void cSyncb () {}} public statique synchronisé csyncb () {}}.
(01) x.issynca () et x.issyncb () ne sont pas accessibles simultanément. Parce que issynca () et issyncb () sont tous deux des verrous de synchronisation qui accèdent au même objet (objet X)!
(02) x.issynca () et y.issynca () sont accessibles en même temps. Parce qu'il n'accède pas au verrou de synchronisation du même objet, X.issynca () accède au verrou de synchronisation de x, tandis que y.issynca () accède au verrouillage de synchronisation de y.
(03) x.csynca () et y.cSyncb () ne sont pas accessibles simultanément. Étant donné que cSynca () et cSyncb () sont deux types statiques, x.csynca () équivaut à quelque chose.issynca (), et y.csyncb () est équivalent à quelque chose.issyncb (), ils partagent un verrou de synchronisation et ne peuvent pas être demandé en même temps.
(04) x.issynca () et quelque chose.csynca () sont accessibles simultanément. Parce que issynca () est une méthode d'instance, x.issynca () utilise le verrouillage de l'objet x; Alors que CSynca () est une méthode statique, quelque chose.csynca () peut comprendre qu'il est utilisé un "verrouillage de classe". Par conséquent, ils sont accessibles simultanément.
Blocage du fil et réveil, notifier, notifier
Dans object.java, les interfaces telles que attendre (), notify () et notifyall () sont définies. La fonction d'attente () est de permettre au thread actuel de saisir un état d'attente, et Wait () permettra également au thread actuel de libérer le verrouillage qu'il conserve. Le rôle de notify () et notifyall () est de réveiller le thread d'attente sur l'objet actuel; notify () consiste à réveiller un seul thread, tandis que notifyall () est de réveiller tous les threads.
Les détails de l'API sur l'attente / le sillage dans la classe d'objets sont les suivants:
notify () - Réveillez un seul thread en attente de ce moniteur d'objet.
notifyall () - Réveillez tous les threads en attente de ce moniteur d'objet.
Wait () - Mettez le thread actuel dans un état "attendre (blocage)" et "Jusqu'à ce que d'autres threads appellent la méthode Notify () ou Notifyall () de cet objet", et le thread actuel est éveillé (entré à "l'état prêt").
attendre (temps de temps long) - Mettez le thread actuel dans un état "attendre (blocage)" et "Jusqu'à ce que d'autres threads appellent la méthode Notify () de l'objet ou NOTIFYALL (), ou dépasser la durée spécifiée", et le thread actuel est éveillé (entré à "l'état prêt").
attendre (temps de temps long, int nanos) - Que le thread actuel soit dans un état "d'attente (blocage)", "jusqu'à ce qu'un autre thread appelle la méthode Notify () de l'objet ou Notifyall (), ou qu'un autre thread interrompt le thread actuel, ou a dépassé un certain temps", et le thread actuel est réveillé (entré à "l'état prêt").
// waitTest.java Code de code source Threada étend Thread {public Threada (String name) {super (name); } public void run () {synchronisé (this) {System.out.println (thread.currentThread (). getName () + "Call notify ()"); // Réveille le thread d'attente actuel Notify (); }}} public class waitTest {public static void main (String [] args) {Threada t1 = new Threada ("t1"); synchronisé (t1) {try {// start "Thread t1" System.out.println (thread.currentThread (). getName () + "start t1"); t1.start (); // Le fil principal attend que T1 se réveille via notify (). System.out.println (thread.currentThread (). GetName () + "wait ()"); t1.Wait (); System.out.println (thread.currentThread (). GetName () + "Continuer"); } catch (InterruptedException e) {e.printStackTrace (); }}}}Sortir
Main start t1main wait () t1 call notify () Main continu
(01) Notez que le "thread principal" sur la figure représente "le fil principal principal". "Thread T1" Représente "Thread T1" démarré dans Waitstest. Et "Lock" représente "un verrou synchrone de l'objet T1".
(02) "Le thread principal" crée un nouveau "thread T1" via un nouveau threada ("T1"). Ensuite, le "verrouillage synchrone de l'objet T1" est obtenu par synchronisé (T1). Ensuite, appelez t1.start () pour démarrer "Thread T1".
(03) "Le thread principal" exécute T1.Wait () pour libérer "Lock de l'objet T1" et entre "Wait (Blocking) State". Attendez que les threads sur les objets T1 le réveillent via notify () ou notifyall ().
(04) Une fois que "Thread T1" est exécuté, le "verrouillage de l'objet actuel" est obtenu par synchronisé (this); Ensuite, appelez Notify () pour réveiller le "thread d'attente sur l'objet actuel", c'est-à-dire réveiller le "thread principal".
(05) Une fois "Thread T1" terminé, relâchez le "verrouillage de l'objet actuel". Immédiatement après, le "Thread principal" acquiert le "verrouillage de l'objet T1" puis s'exécute.
T1.Wait () est la méthode Wait () appelée via "Thread T1", mais l'endroit où T1.Wait () est appelé est dans "Main Thread Main". Le thread principal doit être le "thread actuel", c'est-à-dire l'état en cours d'exécution, avant que T1.Wait () ne puisse être exécuté. Par conséquent, le "thread actuel" à l'heure actuelle est le "Main Thread Main"! Par conséquent, T1.Wait () est de faire attendre le "fil principal", pas "Thread T1"!
package thread.test; classe publique NotifyAllTest {objet statique privé obj = nouveau objet (); public static void main (String [] args) {threada t1 = new Threada ("t1"); Threada t2 = new Threada ("T2"); Threada t3 = new Threada ("T3"); t1.start (); t2.start (); t3.start (); essayez {System.out.println (thread.currentThread (). getName () + "Sleep (3000)"); Thread.Sleep (3000); } catch (InterruptedException e) {e.printStackTrace (); } synchronisé (obj) {System.out.println (thread.currentThread (). getName () + "notifyall ()"); obj.NotifyAll (); // Wake Up t1.t2.t3 ici}} classe statique Threada étend Thread {public threada (String name) {super (name); } public void run () {synchronisé (obj) {try {// printout result system.out.println (thread.currentThread (). getName () + "wait"); // Libérez le verrouillage d'objet OBJ obj.wait (); // Printout Result System.out.println (thread.currentThread (). GetName () + "continu"); } catch (InterruptedException e) {e.printStackTrace (); }}}}}} Sortir:
T1 Waitmain Sleep (3000) T3 Waitt2 Waitmain Notifyall () T2 Continuer3 Continuer1
(01) 3 Threads "T1", "T2" et "T3" ont été créés et démarrés dans le thread principal.
(02) Le fil principal dort pendant 3 secondes par le sommeil (3000). Pendant le sommeil principal pendant 3 secondes, nous supposons que les trois fils "T1", "T2" et "T3" fonctionnent tous. Prenez "T1" comme exemple. Lorsqu'il s'exécute, il exécutera obj.wait () pour attendre que d'autres threads le réveillent via notify () ou nofityall (); De la même manière, "T2" et "T3" attendent également que d'autres fils les réveillent via nofity () ou nofityall ().
(03) Le fil principal dort pendant 3 secondes puis fonctionne. Exécutez obj.notifyall () pour réveiller le fil d'attente sur OBJ, c'est-à-dire réveiller les trois threads "T1", "T2" et "T3". Immédiatement après l'exécution du fil de filetage principal (OBJ), le thread principal libère le "verrouillage OBJ". De cette façon, "T1", "T2" et "T3" peuvent obtenir le "verrouillage Obj" et continuer à fonctionner!
Notifier, notifier et verrouiller la relation
Des fonctions telles que Wait (), Notify () dans l'objet, comme synchronisé, fonctionneront sur "Lock de synchronisation d'objet".
wait () fera attendre le "thread actuel". Parce que le thread entre dans l'état d'attente, le thread doit libérer le "serrure synchrone" maintenue par son serrure, sinon d'autres threads ne pourront pas obtenir le "verrou synchrone" et ne pourront pas fonctionner!
Ok, après que le thread appelle Wait (), il libérera le "verrouillage synchrone" maintenu par sa serrure; Et, selon l'introduction précédente, nous savons que le fil d'attente peut être éveillé par notify () ou notifyall (). Maintenant, pensez à une question: qu'est-ce que notify () basé sur l'éveil du fil d'attente? Ou, quelle est la corrélation entre attendre () et notify ()? La réponse est: basée sur "Lock de synchronisation des objets".
Le fil responsable du réveil du fil d'attente (nous l'appelons "Wake Up Thread"), ne peut réveiller le fil d'attente qu'après avoir obtenu le "verrou synchrone de cet objet" (le verrou de synchronisation ici doit être le même que le verrou de synchronisation du fil d'attente) et d'appeler les méthodes notify () ou notifyall (). Bien que le fil d'attente soit éveillé; Cependant, il ne peut pas être exécuté immédiatement car le fil de réveil contient toujours "Lock synchrone pour l'objet". Vous devez attendre que le fil de réveil libère le "verrouillage de synchronisation de l'objet" avant de pouvoir obtenir le "verrou de synchronisation de l'objet" et continuer à s'exécuter.
En bref, Notify (), Wait () s'appuie sur "Synchronous Lock", qui est maintenu par les verrous d'objet, et chaque objet en a et un seul! C'est pourquoi les fonctions telles que Notify (), Wait () sont définies dans la classe d'objets, pas dans la classe de threads.
Le rendement des concessions du thread
Les concessions du thread font passer le fil de l'état d'exécution à l'état prêt, de sorte que d'autres threads d'attente avec la même priorité peuvent obtenir des droits d'exécution; Cependant, il n'est pas garanti qu'après que les appels de threads actuels rendent (), d'autres threads avec la même priorité obtiendront certainement des droits d'exécution; Il est également possible que le thread actuel entre dans "l'état de course" et continue de s'exécuter.
Créer et attendre
(01) Wait () est de laisser le thread saisir "l'attente (blocage) de l'état" de "l'état en cours d'exécution", tout en ne rendant pas () est de laisser le thread saisir "l'état prêt" de "l'état d'exécution".
(02) Wait () est un verrou de synchronisation qui va libérer l'objet qu'il détient, tandis que la méthode de rendement () ne libérera pas le verrou.
(03) L'attente est la méthode de l'objet, le rendement est la méthode du thread
Sleep de filetage
La fonction de Sleep () est de laisser le thread actuel dormir, c'est-à-dire que le thread actuel entrera de "l'état de course" à "l'état de sommeil (blocage)". Sleep () spécifiera le temps de sommeil et le temps de sommeil du fil sera supérieur à / égal au temps de sommeil; Lorsque le fil est à nouveau éveillé, il passera d'un "état de blocage" à un "état prêt", en attendant que le CPU soit prévu pour s'exécuter.
La différence entre le sommeil et l'attente
La fonction d'attente () est de permettre au thread actuel d'entrer dans l'état "d'attente (blocage) de" l'état en cours d'exécution "et de libérer également le verrouillage de synchronisation. La fonction de Sleep () est de permettre au thread actuel de saisir l'état" Sleep (Blocking) à partir de "l'état en cours d'exécution". (Ce n'est en fait pas très différent)
Wait () libère le verrou de synchronisation de l'objet, tandis que Sleep () ne libère pas le verrouillage
attendre est la méthode de l'objet, le sommeil est la méthode du thread
Rejoindre
Laissez le fil principal attendre et le fil d'enfant peut continuer à fonctionner une fois le thread principal terminé.
interrompre
Utilisé pour terminer un fil bloqué
@OverridePublic void run () {try {while (true) {// Exécuter la tâche ...}} catch (interruptedException ie) {// en raison d'une exception InterruptedException, quittez la boucle while (true) et le thread se termine! }}Dans while (true), l'appel à interrompre () du thread génère une interruption interruptexception. La capture interrompue est à l'extérieur tandis que (vrai), sortant ainsi de la boucle while (vraie)
Termine un fil à l'état en cours d'exécution
@OverRidePublic void run () {while (! IsInterrupted ()) {// Exécuter la tâche ...}}Moyen commun de résilier les fils
@OverridePublic void run () {try {// 1. IsInterrupted () garantit que le fil sera terminé tant que l'interruption est marquée vraie. tandis que (! isInterrupted ()) {// Exécuter la tâche ...}} catch (InterruptedException ie) {// 2. L'exception InterruptedException garantit que lorsqu'une exception InterruptedException se produit, le fil est terminé. }}
Priorité du thread
La plage de priorité de thread en Java est de 1 à 10, et la priorité par défaut est de 5. "Threads de grande priorité" précédera l'exécution sur les "threads à faible priorité". Il existe deux types de threads en Java: le thread utilisateur et le thread de démon. Ils peuvent être distingués par la méthode isdaemon (): si false est renvoyé, cela signifie que le thread est un "thread utilisateur"; Sinon, c'est un "fil de démon". Les threads utilisateur effectuent généralement des tâches au niveau de l'utilisateur, tandis que les threads de démon sont également des "threads backend", qui sont généralement utilisés pour effectuer des tâches d'arrière-plan. Il convient de noter que la machine virtuelle Java quittera une fois le "thread utilisateur" terminé.
Chaque fil a une priorité. "Threads de grande priorité" précédera l'exécution sur les "threads de priorité faible". Chaque fil peut être marqué comme un démon ou un non-daéon. Lors de la création d'un nouveau thread enfant dans un thread principal en cours d'exécution, la priorité du thread de l'enfant est définie pour égaler "la priorité du thread principal qui l'a créé", et "le fil d'enfant sera le thread de démon" quand et seulement si "le thread principal qui l'a créé est un thread de démon".
Lorsqu'une machine virtuelle Java est démarrée, il y a généralement un seul fil non daéon (ce fil est démarré via la méthode principale ()). Le JVM s'exécutera jusqu'à ce que l'une des conditions suivantes se produise, et le JVM mettra fin à l'exécution:
(01) La méthode exit () est appelée, et exit () a l'autorisation d'être exécutée normalement.
(02) Tous les "threads non obligatoires" sont morts (c'est-à-dire qu'il n'y a que des "fils de démon" dans le JVM).
Démon
(01) Le thread principal principal est le thread utilisateur, et le thread enfant T1 qu'il crée est également le thread utilisateur.
(02) T2 est le fil de démon. Lorsque le "MAIN Thread Main" et "Sub-Thread T1" (ce sont les deux threads utilisateur) sont exécutés et que seul le thread Daemon T2 est laissé, le JVM sort automatiquement.
Problèmes des producteurs et des consommateurs
(01) Le producteur ne produit que lorsque l'entrepôt n'est pas plein et arrête la production lorsque l'entrepôt est plein.
(02) Les consommateurs ne peuvent consommer que lorsqu'ils ont des produits en stockage et attendre s'ils ont des entrepôts vides.
(03) Lorsque les consommateurs constatent qu'il n'y a pas de produit à consommer dans l'entrepôt, ils informeront le producteur.
(04) Lorsque les producteurs produisent des produits consommables, ils doivent informer les consommateurs en attente de consommer.
Communication entre les fils
Méthode: mémoire partagée et messagerie
Mémoire partagée: le thread A et le thread B partagent la mémoire, le thread A met à jour la valeur de la variable partagée, la rafraîchisse à la mémoire principale, et le thread B passe à la mémoire principale pour lire les variables mises à jour du thread A. Le processus de communication entier doit passer par la mémoire principale. La synchronisation est effectuée explicitement.
Si une variable est de type volatile, la lecture et l'écriture de la variable seront atomiques. S'il s'agit de multiples opérations volatiles ou d'opérations composites similaires à Volatile ++, ces opérations ne sont pas atomiques dans leur intégralité.
La variable volatile elle-même a les caractéristiques suivantes:
[Visibilité]: Lorsque vous lisez une variable volatile, vous pouvez toujours voir la dernière écriture à la variable volatile (n'importe quel thread).
[Atomicité]: Il a une atomicité pour la lecture / l'écriture de toute variable volatile unique, mais il n'a pas d'atomicité pour les opérations composées similaires à Volatile ++.
Écriture volatile: Lors de l'écriture d'une variable volatile, JMM rincera la variable partagée dans la mémoire locale correspondant au thread à la mémoire principale.
Lire volatile: Lors de la lecture d'une variable volatile, JMM invalide la mémoire locale correspondant au thread. Le thread lira ensuite la variable partagée à partir de la mémoire principale.
Livraison du message: l'envoi d'un message est effectué implicitement avant que le message ne soit accepté.
Threadlocal
ThreadLocal n'est pas utilisé pour résoudre le problème de l'accès multi-thread aux objets partagés. D'une manière générale, l'objet du thread via threadLocal.set () est un objet utilisé par le thread lui-même. Les autres threads n'ont pas besoin d'être accessibles et ne sont pas accessibles. ThreadLocal permet à chaque thread de maintenir son propre objet indépendant. Il n'est pas implémenté via threadLocal.set (), mais un objet créé par le fonctionnement du nouvel objet dans chaque thread. Chaque thread en crée un, pas une copie ou une copie de l'objet. La référence à l'objet nouvellement créé est enregistrée sur la propre carte de chaque thread via ThreadLocal.Set (). Chaque fil a une telle carte. Lorsque threadLocal.get () est exécuté, chaque thread sort l'objet placé de sa propre carte. Par conséquent, ce qui est supprimé est l'objet dans chaque fil. L'instance threadlocal est utilisée comme touche de carte. Si la chose que ThreadLocal.Set () entre est le même objet partagé par plusieurs threads, alors le threadLocal.get () de plusieurs threads obtient toujours l'objet partagé lui-même, et il y a toujours un problème d'accès simultané.
Une implémentation de threadlocal
Importer java.util.collections; import java.util.hashmap; importation java.util.map; / ** * Classe utilisant ThreadLocal * * @author Leizhimin 2010-1-5 10:35:27 * / public class MyThreadlocal {// Définir une variable threadlocal pour enregistrer int ou Integer Data Private Com.lavasoft.test2 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 (); }} 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 ()); }}En fait, ThreadLocal fait ceci:
public t get () {thread t = thread.currentThread (); ThreadLocalmap map = getmap (t); if (map! = null) {threadLocalmap.entry e = map.getEntry (this); if (e! = null) return (t) e.value; } return setInitialValue (); }