Comment utiliser la classe Java ThreadLocal
Comme le montre l'image :
Démarrage rapide
Ensuite, nous utiliserons un exemple simple pour vous montrer l'utilisation de base de ThreadLocal
package cuit.pymjl.thradlocal; /** * @author Pymjl * @version 1.0 * @date 2022/7/1 10:56 **/ public class MainTest { static ThreadLocal<String> threadLocal = new ThreadLocal<>(); static void print(String str) { //打印当前线程中本地内存中本地变量的值 System.out.println(str + " :" + threadLocal.get()); //清除本地内存中的本地变量 threadLocal.remove(); } public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { //设置线程1中本地变量的值 threadLocal.set("thread1 local variable"); //调用打印方法 print("thread1"); //打印本地变量 System.out.println("after remove : " + threadLocal.get()); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { //设置线程1中本地变量的值 threadLocal.set("thread2 local variable"); //调用打印方法 print("thread2"); //打印本地变量 System.out.println("after remove : " + threadLocal.get()); } }); t1.start(); t2.start(); } }
Les résultats en cours sont tels qu'indiqués dans la figure montrée :
Le principe de ThreadLocal
Diagramme de classe associé à ThreadLocal
Jetons d'abord un coup d'œil à la structure du diagramme de classe de la classe associée à ThreadLocal, comme indiqué dans l'image :
On peut voir sur cette image qu'il existe un threadLocals et un ThreadLocals hérités dans la classe Thread, qui sont des variables de type ThreadLocalMap, et ThreadLocalMap est une Hashmap personnalisée. Par défaut, ces deux variables dans chaque thread sont nulles et elles sont créées uniquement lorsque le thread actuel appelle la méthode set ou get de ThreadLocal pour la première fois. En fait, les variables locales de chaque thread ne sont pas stockées dans l'instance ThreadLocal, mais dans la variable threadLocals du thread appelant. En d’autres termes, les variables locales de type ThreadLocal sont stockées dans des espaces mémoire de thread spécifiques. ThreadLocal est un shell d'outil qui place la valeur dans les threadLocals du thread appelant via la méthode set et la stocke lorsque le thread appelant appelle sa méthode get, il la retire de la variable threadLocals du thread actuel pour l'utiliser. Si le thread appelant ne se termine jamais, alors cette variable locale sera toujours stockée dans la variable threadLocals du thread appelant. Par conséquent, lorsque la variable locale n'est pas nécessaire, vous pouvez supprimer la variable locale des threadLocals du thread actuel en appelant le. Remove méthode de la variable ThreadLocal. De plus, pourquoi les threadLocals dans Thread sont-ils conçus comme une structure de carte ? Évidemment parce que chaque thread peut être associé à plusieurs variables ThreadLocal. Ensuite, jetons un coup d'œil au code source de Set, Get et Remove de ThreadLocal
set
public void set(T value) { // 1.获取当前线程(调用者线程) Thread t = Thread.currentThread(); // 2.以当前线程作为key值,去查找对应的线程变量,找到对应的map ThreadLocalMap map = getMap(t); if (map != null) { // 3.如果map不为null,则直接添加元素 map.set(this, value); } else { // 4.否则就先创建map,再添加元素 createMap(t, value); } }
void createMap(Thread t, T firstValue) { /** * 这里是创建一个ThreadLocalMap,以当前调用线程的实例对象为key,初始值为value * 然后放入当前线程的Therad.threadLocals属性里面 */ t.threadLocals = new ThreadLocalMap(this, firstValue); }
ThreadLocalMap getMap(Thread t) { //这里就是直接获取调用线程的成员属性threadlocals return t.threadLocals; }
get
public T get() { // 1.获取当前线程 Thread t = Thread.currentThread(); // 2.获取当前线程的threadlocals,即ThreadLocalMap ThreadLocalMap map = getMap(t); // 3.如果map不为null,则直接返回对应的值 if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } // 4.否则,则进行初始化 return setInitialValue(); }
Ce qui suit est le code de setInitialValue
setInitialValue
的代码
private T setInitialValue() { //初始化属性,其实就是null T value = initialValue(); //获取当前线程 Thread t = Thread.currentThread(); //通过当前线程获取ThreadLocalMap ThreadLocalMap map = getMap(t); //如果map不为null,则直接添加元素 if (map != null) { map.set(this, value); } else { //否则就创建,然后将创建好的map放入当前线程的属性threadlocals createMap(t, value); } //将当前ThreadLocal实例注册进TerminatingThreadLocal类里面 if (this instanceof TerminatingThreadLocal) { TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this); } return value; }
这里我需要补充说明一下TerminatingThreadLocal
/** * A thread-local variable that is notified when a thread terminates and * it has been initialized in the terminating thread (even if it was * initialized with a null value). * 一个线程局部变量, * 当一个线程终止并且它已经在终止线程中被初始化时被通知(即使它被初始化为一个空值)。 */
TerminatingThreadLocal
. Cette classe est nouvelle dans jdk11 et n'existe pas dans jdk8, il n'y a donc aucune description pertinente de cette classe dans de nombreuses analyses de code source sur Internet. J'ai jeté un œil au code source de cette classe, et sa fonction devrait être d'éviter le problème des fuites de mémoire ThreadLocal (si vous êtes intéressé, vous pouvez vérifier le code source, et veuillez me corriger s'il y a des erreurs). Voici l'explication officielle : public void remove() {
//如果当前线程的threadLocals 变量不为空, 则删除当前线程中指定ThreadLocal 实例的本地变量。
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null) {
m.remove(this);
}
}
Copier après la connexionremoverrreee
Summary
Il y a une variable membre nommée threadLocals à l'intérieur de chaque thread. Le type de cette variable est Hash Map, où la clé est la variable ThreadLocal que nous avons définie cette référence, valeur. est la valeur que nous avons définie à l'aide de la méthode set. Les variables locales de chaque thread sont stockées dans la propre variable de mémoire du thread threadLocals. Si le thread actuel ne meurt jamais, ces variables locales existeront toujours, cela peut donc provoquer un débordement de mémoire. Par conséquent, n'oubliez pas d'appeler la méthode Remove de ThreadLocal pour la supprimer après. utiliser. Variables locales dans threadLocals correspondant au thread. Fuite de mémoire ThreadLocalPourquoi une fuite de mémoire se produit-elle ? public void remove() { //如果当前线程的threadLocals 变量不为空, 则删除当前线程中指定ThreadLocal 实例的本地变量。 ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) { m.remove(this); } }
ThreadLocalMap utilise la référence faible de ThreadLocal comme clé. Si un ThreadLocal n'a pas de référence forte externe pour s'y référer, alors le ThreadLocal sera inévitablement recyclé lors du GC du système De cette façon, une entrée avec un null. key apparaîtra dans le ThreadLocalMap , il n'y a aucun moyen d'accéder à la valeur de ces entrées avec des clés nulles Si le fil de discussion en cours continue à se terminer, les valeurs de ces entrées avec des clés nulles auront toujours une chaîne de référence forte : Thread Ref -> Thread - > ThreaLocalMap -> La valeur de l'entrée ne peut jamais être recyclée, provoquant une fuite de mémoire.
En fait, cette situation a été prise en compte dans la conception de ThreadLocalMap, et quelques mesures de protection ont été ajoutées : toutes les valeurs avec des clés nulles dans le thread ThreadLocalMap seront effacées lors de get(), set() et delete () de ThreadLocal. Cependant, ces mesures préventives passives ne garantissent pas qu'il n'y aura pas de fuites de mémoire :- L'utilisation de ThreadLocal statique prolonge le cycle de vie de ThreadLocal, ce qui peut entraîner des fuites de mémoire
- L'allocation utilise ThreadLocal et n'appelle plus get( ), set(), remove(), cela provoquera des fuites de mémoire
Si vous utilisez des références fortes : nous savons que le cycle de vie de ThreadLocalMap est fondamentalement le même que le cycle de vie de Thread. Si le thread actuel n'est pas terminé, alors ThreadLocalMap ne sera jamais recyclé par GC et ThreadLocalMap détient une forte référence. référence à ThreadLocal, alors ThreadLocal Il ne sera pas recyclé. Lorsque le cycle de vie du thread est long, s'il n'est pas supprimé manuellement, cela provoquera une accumulation de kv, ce qui entraînera un MOO
Si vous utilisez des références faibles : objets dans des références faibles. avoir une période de déclaration courte, car dans le système Pendant le GC, tant qu'une référence faible est trouvée, l'objet sera recyclé, qu'il y ait ou non suffisamment d'espace sur le tas. Lorsque la référence forte de ThreadLocal est recyclée, la référence faible détenue par ThreadLocalMap sera également recyclée. Si kv n'est pas supprimé manuellement, cela provoquera une accumulation de valeur et conduira également à OOM
. l'utilisation de références faibles peut au moins garantir que le MOO ne sera pas causé par l'accumulation de clés de carte et que la valeur correspondante peut être effacée lors du prochain appel via les méthodes Remove, Get et Set. On peut voir que la cause première des fuites de mémoire ne réside pas dans les références faibles, mais que le cycle de vie de ThreadLocalMap est aussi long que celui de Thread, provoquant une accumulation
Solution
Puisque la racine du problème est l'accumulation de valeurs. provoquant du MOO, nous pouvons alors prescrire le bon médicament et nous assurer qu'il est utilisé après chaque utilisation. Il suffit d'appeler la méthode remove()
de ThreadLocal pour le nettoyer.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Guide du nombre parfait en Java. Nous discutons ici de la définition, comment vérifier le nombre parfait en Java ?, des exemples d'implémentation de code.

Guide de Weka en Java. Nous discutons ici de l'introduction, de la façon d'utiliser Weka Java, du type de plate-forme et des avantages avec des exemples.

Guide du nombre de Smith en Java. Nous discutons ici de la définition, comment vérifier le numéro Smith en Java ? exemple avec implémentation de code.

Dans cet article, nous avons conservé les questions d'entretien Java Spring les plus posées avec leurs réponses détaillées. Pour que vous puissiez réussir l'interview.

Java 8 présente l'API Stream, fournissant un moyen puissant et expressif de traiter les collections de données. Cependant, une question courante lors de l'utilisation du flux est: comment se casser ou revenir d'une opération FOREAK? Les boucles traditionnelles permettent une interruption ou un retour précoce, mais la méthode Foreach de Stream ne prend pas directement en charge cette méthode. Cet article expliquera les raisons et explorera des méthodes alternatives pour la mise en œuvre de terminaison prématurée dans les systèmes de traitement de flux. Lire plus approfondie: Améliorations de l'API Java Stream Comprendre le flux Forach La méthode foreach est une opération terminale qui effectue une opération sur chaque élément du flux. Son intention de conception est

Guide de TimeStamp to Date en Java. Ici, nous discutons également de l'introduction et de la façon de convertir l'horodatage en date en Java avec des exemples.

Les capsules sont des figures géométriques tridimensionnelles, composées d'un cylindre et d'un hémisphère aux deux extrémités. Le volume de la capsule peut être calculé en ajoutant le volume du cylindre et le volume de l'hémisphère aux deux extrémités. Ce tutoriel discutera de la façon de calculer le volume d'une capsule donnée en Java en utilisant différentes méthodes. Formule de volume de capsule La formule du volume de la capsule est la suivante: Volume de capsule = volume cylindrique volume de deux hémisphères volume dans, R: Le rayon de l'hémisphère. H: La hauteur du cylindre (à l'exclusion de l'hémisphère). Exemple 1 entrer Rayon = 5 unités Hauteur = 10 unités Sortir Volume = 1570,8 unités cubes expliquer Calculer le volume à l'aide de la formule: Volume = π × r2 × h (4

Spring Boot simplifie la création d'applications Java robustes, évolutives et prêtes à la production, révolutionnant le développement de Java. Son approche "Convention sur la configuration", inhérente à l'écosystème de ressort, minimise la configuration manuelle, allo
