Maison > Java > javaDidacticiel > Analyse et introduction au principe d'implémentation de ThreadLocal (avec code)

Analyse et introduction au principe d'implémentation de ThreadLocal (avec code)

不言
Libérer: 2019-02-16 13:37:47
avant
3007 Les gens l'ont consulté

Ce que cet article vous apporte est une analyse et une introduction au principe de mise en œuvre de ThreadLocal (avec code). Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.

ThreadLocal, une variable locale de thread, est utilisée pour conserver une copie indépendante de la variable pour chaque thread qui l'utilise. Cette variable n'est valide que pendant le cycle de vie du thread. Et contrairement au mécanisme de verrouillage, qui échange du temps contre de l'espace, ThreadLocal ne dispose d'aucun mécanisme de verrouillage. Il échange de l'espace contre du temps pour garantir la sécurité des threads des variables.

Cet article analyse le principe d'implémentation de ThreadLocal à partir du code source.

Premier aperçu de la structure du diagramme de classes ThreadLocal

 

SuppliedThreadLocal est principalement utilisé par JDK1.8 pour étendre la prise en charge des expressions Lambda. Si vous êtes intéressé, veuillez vous référer à Baidu.

ThreadLocalMap est la classe interne statique de ThreadLocal et est également la classe qui enregistre réellement les variables.

Entry est la classe interne statique de ThreadLocalMap. ThreadLocalMap contient un tableau Entry, avec ThreadLocal comme clé et la variable comme valeur, encapsulant une Entry.

Le diagramme suivant explique brièvement la relation entre Thread, ThreadLocal, ThreadLocalMap et Entry.

 

Expliquez l'image ci-dessus :

  1. Un Thread a un objet ThreadLocalMap

  2. ThreadLocalMap a un tableau Entry

  3. Chaque entrée a k--v

  4. La clé d'entrée est un objet ThreadLocal spécifique

Les principales méthodes sont analysées ci-dessous.

1. ensemble()

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
Copier après la connexion

On peut le voir ici : un Thread n'a qu'un seul objet ThreadLocalMap ; la valeur stockée spécifique est appelée set() de ThreadLocalMap, et la clé de paramètre transmise est l'objet ThreadLocal actuel.

Regardez la méthode set() de ThreadLocalMap :

private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1); // 1

            for (Entry e = tab[i];  // 2
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value); // 3
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold) // 4
                rehash();
        }
Copier après la connexion

  1. Calculez l'index du tableau en prenant le modulo du hashCode de la clé et de la capacité du tableau -1

  2. Commencez à parcourir à partir de l'index actuel et effacez ceux non valides où la clé est null Entry

  3. Encapsulez K-V en tant qu'Entrée et placez-le dans le tableau

  4. pour déterminer si cela est nécessaire Développez le tableau Entry. La valeur du seuil est de 2/3 de la capacité du réseau.

Jetez un œil à la méthode d'expansion resize() :

private void resize() {
            Entry[] oldTab = table;
            int oldLen = oldTab.length;
            int newLen = oldLen * 2;
            Entry[] newTab = new Entry[newLen];
            int count = 0;

            for (int j = 0; j < oldLen; ++j) {
                Entry e = oldTab[j];
                if (e != null) {
                    ThreadLocal<?> k = e.get();
                    if (k == null) {
                        e.value = null; // Help the GC
                    } else {
                        int h = k.threadLocalHashCode & (newLen - 1);
                        while (newTab[h] != null)
                            h = nextIndex(h, newLen);
                        newTab[h] = e;
                        count++;
                    }
                }
            }

            setThreshold(newLen);
            size = count;
            table = newTab;
        }
Copier après la connexion

L'essentiel ici est d'augmenter la capacité de 2 fois celle d'origine. Parcourez ensuite l'ancien tableau et recalculez la position de l'entrée dans le nouveau tableau en fonction de la nouvelle capacité du tableau.

2. get()

La méthode get() de ThreadLocal est la suivante :

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t); 
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this); 
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
Copier après la connexion

La méthode getEntry() de ThreadLocalMap est la suivante :

private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1); // 1
            Entry e = table[i];
            if (e != null && e.get() == key) // 2
                return e;
            else
                return getEntryAfterMiss(key, i, e); //3
        }

private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;

            while (e != null) { //4
                ThreadLocal<?> k = e.get();
                if (k == key)
                    return e;
                if (k == null)
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }
Copier après la connexion

  1. Calculer l'index

  2. L'entrée sur l'index actuel n'est pas vide et la clé est la même, retournez directement

  3. Sinon, allez dans l'index adjacent pour rechercher

  4. en boucle Si une clé invalide est trouvée, elle sera effacée. Terminez le cycle une fois trouvé.

3. supprimer()

public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
private void remove(ThreadLocal<?> key) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }
Copier après la connexion

La méthode de traitement est similaire à la recherche et à l'enregistrement. Après avoir supprimé l'entrée correspondante, les éléments invalides avec des clés nulles seront supprimés.

Remarque

static class Entry extends WeakReference<ThreadLocal<?>> {}
Copier après la connexion

ThreadLocal peut avoir des problèmes de MOO. Étant donné que ThreadLocalMap utilise la référence faible de ThreadLocal comme clé, lorsque GC se produit, la clé est recyclée, nous ne pouvons donc pas accéder à l'élément de valeur avec une clé nulle. Si la valeur elle-même est un objet plus grand, alors si le thread ne se termine pas, la valeur sera Il n'a jamais pu être recyclé. Surtout lorsque nous utilisons le pool de threads, les threads sont réutilisés et les threads ne seront pas tués. De cette façon, lorsque la référence faible ThreadLocal est recyclée, la valeur ne sera pas recyclée.

Lors de l'utilisation de ThreadLocal, la méthode ThreadLocal.remove() doit être explicitement appelée à la fin du code logique du thread.

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!

Étiquettes associées:
source:cnblogs.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal