Mots-clés synchronisés
Lorsqu'un mot-clé de langue Java est utilisé pour modifier une méthode ou un bloc de code, il peut s'assurer qu'au plus un thread exécute le code en même temps.
Lorsque deux threads simultanés accèdent à ce bloc de code synchronisé synchronisé (ce) dans le même objet d'objet, un seul thread peut être exécuté dans un temps. Un autre thread doit attendre que le thread actuel exécute ce bloc de code avant de pouvoir exécuter le bloc de code.
Cependant, lorsqu'un thread accède à un bloc de code de synchronisation synchronisé (this) d'un objet, un autre thread peut toujours accéder au bloc de code de synchronisation non synchronisé (this) dans cet objet.
Il est particulièrement essentiel que lorsqu'un thread accède à un bloc de code de synchronisation synchronisé (this) d'un objet, d'autres threads bloqueront l'accès à tous les autres blocs de code de synchronisation synchronisés (this) dans l'objet.
Le troisième exemple s'applique également à d'autres blocs de code synchrones. Autrement dit, lorsqu'un thread accède à un bloc de code de synchronisation synchronisé (ce) synchronisation d'un objet, il obtient le verrouillage de l'objet de cet objet. En conséquence, d'autres threads accès à toutes les parties de code synchrones de l'objet objet sont temporairement bloqués.
Les règles ci-dessus s'appliquent également aux autres verrous d'objets.
Exemple de code
Package Test160118; classe publique TestSynchronized {public static void main (String [] args) {sy sy = new Sy (0); Sy sy2 = nouveau sy (1); sy.start (); sy2.start (); }} class sy étend Thread {private int flag; objet statique x1 = nouvel objet (); objet statique x2 = nouvel objet (); public sy (int flag) {this.flag = drapeau; } @Override public void run () {System.out.println (Flag); try {if (flag == 0) {synchronisé (x1) {System.out.println (Flag + "Verrouillé x1"); Thread.Sleep (1000); synchronisé (x2) {System.out.println (Flag + "Verrouillé x2"); } System.out.println (Flag + "Release X1 et X2"); }} if (flag == 1) {synchronisé (x2) {System.out.println (Flag + "Verrouillé x2"); Thread.Sleep (1000); synchronisé (x1) {System.out.println (Flag + "Verrouillé x1"); } System.out.println (Flag + "Release X1 et X2"); }}} catch (InterruptedException e) {e.printStackTrace (); }}}
Principe d'implémentation de filetage sans serrure threadlocal
Que peut faire ThreadLocal?
Cette phrase est difficile à dire. Jetons un coup d'œil à certaines des difficultés rencontrées dans le projet réel: lorsque vous appelez certaines méthodes en fonction de certains paramètres du projet, puis la méthode appelle la méthode, puis appelez la méthode à travers les objets, ces méthodes peuvent utiliser certains paramètres similaires, par exemple, a nécessite des paramètres A, B et C dans A. Après un appel B, B nécessite des paramètres B et C dans B, et b appelle C Méthodes A et B. À l'heure actuelle, tous les paramètres doivent être transmis à B. et ainsi de suite. S'il existe de nombreuses méthodes appelées, les paramètres deviendront de plus en plus. De plus, lorsque le programme doit ajouter des paramètres, vous devez ajouter des paramètres aux méthodes pertinentes une par une. Oui, c'est très gênant. Je crois que vous l'avez rencontré. Il s'agit également de quelques méthodes de traitement courantes pour les objets dans le langage C. Cependant, notre méthode de traitement simple consiste à l'envelopper dans un objet et à la transmettre. Ce problème peut être résolu en ajoutant les attributs de l'objet. Cependant, les objets sont généralement significatifs, donc parfois un emballage d'objets simples ajoute des attributs non pertinents étendus pour rendre notre définition de classe très étrange, donc dans ces cas lorsque nous structurons des programmes complexes comme celui-ci, nous utilisons des lunettes similaires à la portée pour les gérer. Les noms et les utilisations sont plus courants. Semblable aux applications Web, il y aura des étendues au contexte, de la session, de la demande et des niveaux de page. ThreadLocal peut également résoudre ce type de problème, mais il n'est pas très approprié de résoudre ce type de problème. Lorsqu'ils sont confrontés à ces problèmes, il ne les passe généralement pas dans les premiers stades de la portée et de l'objet, et pense que les paramètres ne seront pas ajoutés. , Lors de l'ajout de paramètres, j'ai constaté qu'il y avait de nombreux endroits à modifier. Afin de ne pas détruire la structure du code, il est possible qu'il y ait trop de paramètres, ce qui a réduit la lisibilité du code de la méthode. ThreadLocal est ajouté pour le gérer. Par exemple, lorsqu'une méthode appelle une autre méthode, 8 paramètres sont passés et l'un des paramètres est transmis en appelant la nième méthode couche par couche. À l'heure actuelle, la dernière méthode doit ajouter un paramètre. Il est naturel que la première méthode devienne 9 paramètres, mais pour le moment, les méthodes pertinentes seront impliquées, ce qui rend le code gonflé.
Le threadlocal mentionné ci-dessus est le but de réparer le problème de la perte des moutons, mais ce n'est pas un moyen particulièrement recommandé de l'utiliser. Il a également des méthodes similaires pour l'utiliser, c'est-à-dire qu'il existe de nombreux appels dynamiques au niveau du cadre, et certains protocoles doivent être respectés pendant le processus d'appel. Bien que nous essayions d'être universels, de nombreux paramètres étendus ne sont pas faciles à considérer lors de la définition du protocole, et la version est également mise à niveau à tout moment. Cependant, lorsque le cadre est étendu, l'interface est également une compatibilité universelle et vers l'arrière. Nous avons besoin de contenus étendus pour être pratiques et simples.
En termes simples, ThreadLocal transforme certaines extensions de système complexes en définitions simples, ce qui rend les parties impliquées dans les paramètres connexes. Voici notre exemple:
Dans le gestionnaire de transactions de Spring, la connexion obtenue par la source de données est placée dans ThreadLocal. Une fois le programme exécuté, la connexion est obtenue à partir de ThreadLocal, puis l'engagement et le retour en arrière sont effectués. En usage, il est nécessaire de s'assurer que la connexion obtenue par le programme via DataSource est obtenue à partir du printemps. Pourquoi une telle opération? Parce que le code commercial est complètement déterminé par l'application et que le cadre ne peut pas exiger que le code commercial soit écrit, sinon le cadre perdra l'avantage de ne pas permettre au code commercial de gérer la connexion. Une fois le code commercial réduit, le ressort ne passera pas de connexion à la zone du code commercial. Il doit être sauvé dans un endroit. Lorsque la couche sous-jacente passe par Ibatis et le printemps. Lorsqu'un cadre tel que JDBC obtient la connexion de la même source de données, elle appellera pour l'obtenir en fonction des règles convenues au printemps. Étant donné que le processus d'exécution est traité dans le même thread, la même connexion est obtenue pour garantir que la connexion utilisée est la même pendant les opérations de validation, de recul et d'entreprise, car seule la même connexion peut garantir les transactions, sinon la base de données elle-même n'est pas prise en charge.
En fait, dans de nombreuses applications de programmation simultanées, le threadlocal joue un rôle important très important. Il n'ajoute pas de verrous et enferme facilement les fils de manière transparente, sans nécessiter de réallocation d'espace à chaque fois comme les variables locales. Étant donné que de nombreux espaces sont en file d'attente, ils peuvent utiliser à plusieurs reprises des tampons de file privilégiés.
Comment utiliser ThreadLocal?
Définissez une variable threadlocal dans n'importe quel emplacement approprié dans le système, qui peut être défini comme un type statique public (un objet threadlocal est directement nouveau). Si vous souhaitez y installer des données, utilisez SET (Object), utilisez l'opération GET () et utilisez la suppression () lors de la suppression des éléments. Les autres méthodes sont des méthodes non publiques et ne sont pas recommandées.
Voici un exemple simple (Code Snippet 1):
classe publique threadLocalTest2 {public final static threadlocal <string> test_thread_name_local = new ThreadLocal <string> (); public final static threadlocal <string> test_thread_value_local = new ThreadLocal <string> (); public static void main (String [] args) {for (int i = 0; i <100; i ++) {final string name = "thread- [" + i + "]"; Valeur de chaîne finale = String.ValueOf (i); nouveau thread () {public void run () {try {test_thread_name_local.set (name); Test_thread_value_local.set (valeur); calla (); } enfin {test_thread_name_local.remove (); Test_thread_value_local.remove (); } } } }.commencer(); }} public static void calla () {callb (); } public static void callb () {new ThreadLocalTest2 (). callc (); } public void callc () {calld (); } public void calld () {System.out.println (test_thread_name_local.get () + "/ t = / t" + test_thread_value_local.get ()); }}Ici, nous simulons 100 threads pour accéder respectivement au nom et à la valeur. Les valeurs du nom et de la valeur sont délibérément définies au milieu pour voir s'il y a un problème de concurrence. Grâce à la sortie, nous pouvons voir que la sortie du thread n'est pas sortie dans l'ordre, ce qui signifie qu'il est exécuté en parallèle, et le nom et la valeur du thread peuvent être correspondants. Au milieu, à travers plusieurs méthodes, les paramètres ne sont pas passés dans l'appel réel, alors comment obtenir les variables correspondantes. Cependant, dans les systèmes réels, ils traversent souvent des classes. Ici, ils ne sont simulés que dans une classe. En fait, les classes croisées sont le même résultat. Vous pouvez les simuler vous-même.
Je crois qu'après avoir vu cela, de nombreux programmeurs sont très intéressés par le principe du threadlocal. Voyons comment cela se fait. Bien que les paramètres ne soient pas passés, ils peuvent l'utiliser comme des variables locales. C'est en effet assez magique. En fait, vous pouvez dire qu'il s'agit d'une méthode de réglage. Lorsque vous voyez que le nom doit être lié au thread, puis disons moins de bêtises, jetons un coup d'œil à son code source. Puisque nous utilisons le plus de jeu, obtenez et supprimez, puis commencez par Set:
La méthode set (t obj) est (Code Snippet 2):
public void set (t valeur) {thread t = thread.currentThread (); ThreadLocalmap map = getmap (t); if (map! = null) map.set (this, valeur); else CreateMap (t, valeur);}Tout d'abord, le thread actuel est obtenu, le même que la supposition, puis il existe une méthode GetMap, qui passe dans le thread actuel. Nous pouvons d'abord comprendre que cette carte est une carte liée au fil. Ensuite, s'il n'est pas vide, effectuez l'opération définie. Lorsque vous le suivez, vous constaterez que cela est similaire à l'opération de put de HashMap, c'est-à-dire qu'un élément de données est écrit dans la carte. S'il est vide, la méthode CreateMap est appelée. Après être entré, jetez un coup d'œil (Code Snippet 3):
void CreateMap (Thread T, T FirstValue) {T.ThreadLocals = new ThreadLocalmap (This, FirstValue);}Cashback crée un threadLocalmap et écrit les paramètres passés et le threadlocal actuel comme structure KV (Code Snippet 4):
ThreadLocalmap (ThreadLocal FirstKey, Object FirstValue) {table = new Entry [initial_capacity]; int i = firstKey.ThreadLocalHashCode & (initial_capacity - 1); table [i] = nouvelle entrée (FirstKey, FirstValue); taille = 1; setThreshold (initial_capacity);}Ceci n'est pas expliqué ici les détails structurels de ThreadLocalmap. Il vous suffit de savoir que son implémentation est similaire à HashMap. Il existe de nombreuses méthodes qui n'ont pas de carte d'implémentations, car il ne veut pas que vous obteniez une carte via certaines méthodes (telles que la réflexion) pour la faire fonctionner davantage. Il s'agit d'une classe intérieure statique dans le type de threadlocal, par défaut, et seules les classes sous java.lang peuvent y faire référence, afin que vous puissiez penser que le thread peut y faire référence.
Regardons en arrière la méthode GetMap, car je sais seulement que la carte obtenue est liée au thread, et via Code Snippet 3, il y a un T.ThreadLocalmap = New ThreadLocalmap (ceci, FirstValue), je crois que vous devez probablement comprendre que cette variable devrait provenir de thread. Entrons selon la méthode GetMap:
ThreadLocalmap getmap (Thread t) {return t.ThreadLocals;}Oui, il provient du fil, et ce fil est le thread actuel, alors allez et regardez la définition:
ThreadLocal.ThreadLocalmap ThreadLocals = null;
Cette propriété est dans la classe Thread, c'est-à-dire que chaque thread a un threadlocalmap par défaut, qui est utilisé pour stocker des variables locales au niveau du thread. Habituellement, vous ne pouvez pas y attribuer de valeurs, car ces affectations sont généralement peu sûres.
Cela semble un peu désordonné, ne vous inquiétez pas, regardons en arrière et explorons les idées:
1. Il y a une propriété dans le thread qui est quelque chose de similaire à HashMap, mais son nom est ThreadLocalmap. Cette propriété est de type par défaut, donc toutes les classes dans le même package peuvent être référencées. Parce qu'il s'agit d'une variable locale de thread, chaque thread a sa propre carte séparée, qui n'influence pas les uns avec les autres, donc même si ThreadLocal est défini comme des threads statiques, il n'y aura pas de conflit.
2. ThreadLocal et Thread sont sous le même package. Vous pouvez vous référer à cette classe et opérer dessus. À l'heure actuelle, chaque threadlocal en définit un, utilisez-le comme la clé et la valeur que vous transmettez comme valeur, et c'est le threadlocal que vous définissez. Par conséquent, différentes variables threadlocal utilisent l'ensemble et les données entre elles ne seront pas en conflit, car leurs clés sont différentes. Bien sûr, après que le même threadlocal ait fait deux opérations de set, la dernière fois sera la dominante.
3. Pour résumer, lorsque les threads sont parallèles, le threadlocal peut être utilisé comme des variables locales, et est en sécurité, et les données entre les différentes variables threadlocal n'ont aucun conflit.
Continuons à regarder la méthode Get et supprimez la méthode, c'est en fait simple:
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 ();}En appelant la méthode GetMap en fonction du thread actuel, c'est-à-dire que T.ThreadLocalmap est appelé, puis en recherchant dans la carte, notez que la carte se trouve avec l'entrée, c'est-à-dire la structure de base de KV, car vous n'écrivez que la valeur elle-même. Vous pouvez voir que Map.GetEntry est également obtenu à travers cela.
La même méthode de suppression est:
public void retire () {threadLocalmap m = getmap (thread.currentThread ()); if (m! = null) M.Remove (this);}De plus, la carte est obtenue sur la base du thread actuel. S'il n'est pas vide, retirez et retirez-le à travers cela.
En outre (2013-6-29), quels sont les pièges de l'oubli d'écrire? Quels pièges y a-t-il dans ce threadlocal? Vous devriez voir à partir de l'exemple précédent que l'objet lié au threadlocal est lié à une carte, et cette carte est une propriété du thread. Ensuite, il y a un problème que si vous ne vous supprimez pas ou si vous ne savez pas quand supprimer dans votre propre programme, le fil ne sera pas déconnecté et les données définies ne seront pas déconnectées.
D'un autre côté, à moins que vous ne compreniez clairement où cet objet doit être défini et où supprimer. S'il est vague, il est très probable que votre code n'ira pas à la position de suppression ou ne provoquera pas des problèmes logiques. De plus, s'il n'est pas supprimé, vous devez attendre que le fil soit déconnecté. Dans de nombreux serveurs d'applications, les threads sont réutilisés car il y a encore des frais généraux dans les threads d'allocation du noyau, il est donc difficile pour les threads d'être déconnectés dans ces applications. Ensuite, les données écrites sur ThreadLocal ne sont naturellement pas faciles à être déconnectées. Ceux-ci peuvent être accidentellement masqués et utilisés lorsque nous utilisons des cadres open source, ce qui peut causer des problèmes. Enfin, j'ai constaté que lorsque OOM, les données proviennent en fait de ThreadLocalmap. Je ne sais pas où ces données sont définies, vous devriez donc prêter attention à cette fosse, et plus d'une personne est tombée dans cette fosse.