Heim  >  Artikel  >  Java  >  Analyse und Einführung in das Implementierungsprinzip von ThreadLocal (mit Code)

Analyse und Einführung in das Implementierungsprinzip von ThreadLocal (mit Code)

不言
不言nach vorne
2019-02-16 13:37:472921Durchsuche

Dieser Artikel bietet Ihnen eine Analyse und Einführung in das Implementierungsprinzip von ThreadLocal (mit Code). Ich hoffe, dass er für Sie hilfreich ist.

ThreadLocal, eine lokale Thread-Variable, wird verwendet, um eine unabhängige Kopie der Variablen für jeden Thread zu verwalten, der sie verwendet. Diese Variable ist nur während des Lebenszyklus des Threads gültig. Und im Gegensatz zum Sperrmechanismus, der Zeit gegen Speicherplatz tauscht, verfügt ThreadLocal über keinen Sperrmechanismus. Es tauscht Platz gegen Zeit, um die Thread-Sicherheit von Variablen zu gewährleisten.

Dieser Artikel analysiert das Implementierungsprinzip von ThreadLocal anhand des Quellcodes.

Erster Blick auf die ThreadLocal-Klassendiagrammstruktur

 

SuppliedThreadLocal wird hauptsächlich von JDK1.8 verwendet, um die Unterstützung für Lambda-Ausdrücke zu erweitern. Wenn Sie interessiert sind, wenden Sie sich bitte an Baidu.

ThreadLocalMap ist die statische innere Klasse von ThreadLocal und auch die Klasse, die tatsächlich Variablen speichert.

Entry ist die statische innere Klasse von ThreadLocalMap. ThreadLocalMap enthält ein Entry-Array mit ThreadLocal als Schlüssel und der Variablen als Wert, wodurch ein Eintrag gekapselt wird.

Das folgende Diagramm erläutert kurz die Beziehung zwischen Thread, ThreadLocal, ThreadLocalMap und Entry.

 

Erklären Sie das Bild oben:

  1. Ein Thread hat ein ThreadLocalMap-Objekt

  2. ThreadLocalMap hat ein Entry-Array

  3. Jeder Eintrag hat k--v

  4. Der Schlüssel von Eintrag ist ein bestimmtes ThreadLocal-Objekt

Die wichtigsten Methoden werden im Folgenden analysiert.

1. set()

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

Hier ist zu sehen: Ein Thread besitzt nur ein ThreadLocalMap-Objekt; der spezifische gespeicherte Wert heißt set() von ThreadLocalMap und der übergebene Parameterschlüssel ist das aktuelle ThreadLocal-Objekt.

Schauen Sie sich die set()-Methode von ThreadLocalMap an:

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();
        }

  1. Berechnen Sie den Array-Index, indem Sie das Modulo des Hash-Codes des Schlüssels und die Array-Kapazität -1 nehmen

  2. Beginnen Sie mit dem Durchlaufen vom aktuellen Index und löschen Sie ungültige Einträge mit Nullschlüsseln

  3. Kapseln Sie K-V als Eintrag und fügen Sie es in das Array ein

  4. um zu bestimmen, ob Eintrag ist eine Array-Erweiterung erforderlich. Der Schwellenwert beträgt 2/3 der Array-Kapazität.

Schauen Sie sich die Erweiterungsmethode resize() an:

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;
        }

Hier geht es vor allem darum, die Kapazität auf das 2-fache der Originalgröße zu erweitern. Anschließend durchlaufen Sie das alte Array und berechnen die Position des Eintrags im neuen Array basierend auf der neuen Array-Kapazität neu.

2. get()

Die get()-Methode von ThreadLocal lautet wie folgt:

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();
    }

Die getEntry()-Methode von ThreadLocalMap lautet wie folgt:

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;
        }

  1. Index berechnen

  2. Der Eintrag im aktuellen Index ist nicht leer und der Schlüssel ist der gleich, direkt zurück

  3. Andernfalls suchen Sie nach

  4. im nebenstehenden Index in a Wird ein ungültiger Schlüssel gefunden, wird dieser gelöscht. Beenden Sie den Zyklus, wenn er gefunden wird.

3. Remove()

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;
                }
            }
        }

Die Verarbeitungsmethode ähnelt dem Suchen und Speichern. Nach dem Löschen des entsprechenden Eintrags werden ungültige Elemente mit Nullschlüsseln entfernt.

Hinweis

static class Entry extends WeakReference<ThreadLocal<?>> {}

ThreadLocal hat möglicherweise OOM-Probleme. Da ThreadLocalMap die schwache Referenz von ThreadLocal als Schlüssel verwendet, wird der Schlüssel beim Auftreten von GC recycelt, sodass wir nicht mit einem Nullschlüssel auf das Wertelement zugreifen können. Wenn der Wert selbst ein größeres Objekt ist, wird der Thread nie beendet Der Wert wird sein. Es konnte nie recycelt werden. Insbesondere wenn wir den Thread-Pool verwenden, werden Threads wiederverwendet und Threads werden nicht beendet. Auf diese Weise wird der Wert nicht recycelt, wenn die schwache ThreadLocal-Referenz recycelt wird.

Bei Verwendung von ThreadLocal muss die Methode ThreadLocal.remove() explizit aufgerufen werden, wenn der Thread-Logikcode endet.

Das obige ist der detaillierte Inhalt vonAnalyse und Einführung in das Implementierungsprinzip von ThreadLocal (mit Code). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:cnblogs.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen