Heim  >  Artikel  >  Java  >  So verwenden Sie die Java ThreadLocal-Klasse

So verwenden Sie die Java ThreadLocal-Klasse

王林
王林nach vorne
2023-05-14 18:49:061092Durchsuche

    Wie im Bild gezeigt:

    So verwenden Sie die Java ThreadLocal-Klasse

    # 🎜🎜#Schnellstart

    Als Nächstes zeigen wir Ihnen anhand eines einfachen Beispiels die grundlegende Verwendung von 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();
        }
    }

    # 🎜🎜# Das laufende Ergebnis ist wie in der Abbildung dargestellt:

    So verwenden Sie die Java ThreadLocal-KlasseDas Prinzip von ThreadLocal

    ThreadLocal-bezogenes Klassendiagramm # 🎜🎜#

    Schauen wir uns zunächst die Klassendiagrammstruktur der ThreadLocal-bezogenen Klasse an, wie in der Abbildung dargestellt:

    # 🎜🎜# Wie aus dieser Abbildung ersichtlich ist, gibt es in der Thread-Klasse ein threadLocals und ein inheritableThreadLocals. Beide sind Variablen vom Typ ThreadLocalMap, und ThreadLocalMap ist eine benutzerdefinierte Hashmap. Standardmäßig sind diese beiden Variablen in jedem Thread null und werden nur erstellt, wenn der aktuelle Thread die Set- oder Get-Methode von ThreadLocal zum ersten Mal aufruft. Tatsächlich werden die lokalen Variablen jedes Threads nicht in der ThreadLocal-Instanz gespeichert, sondern in der threadLocals-Variablen des aufrufenden Threads. Mit anderen Worten, lokale Variablen vom Typ ThreadLocal werden in einem bestimmten Thread-Speicherbereich gespeichert. ThreadLocal ist eine Tool-Shell, die den Wert über die Set-Methode in die ThreadLocals des aufrufenden Threads einfügt und speichert. Wenn der aufrufende Thread seine Get-Methode aufruft, entnimmt er ihn zur Verwendung aus der ThreadLocals-Variablen des aktuellen Threads. Wenn der aufrufende Thread nie beendet wird, wird diese lokale Variable immer in der Variablen threadLocals des aufrufenden Threads gespeichert. Wenn die lokale Variable nicht benötigt wird, können Sie die lokale Variable daher aus den threadLocals des aktuellen Threads löschen Remove-Methode der ThreadLocal-Variablen. Warum sind die ThreadLocals in Thread außerdem als Kartenstruktur konzipiert? Offensichtlich, weil jeder Thread mehreren ThreadLocal-Variablen zugeordnet werden kann. Schauen wir uns als Nächstes den Quellcode von ThreadLocals Set, Get und Remove an code>setInitialValues Code

        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);
            }
        }
    So verwenden Sie die Java ThreadLocal-KlasseIch muss hier eine zusätzliche Erklärung hinzufügen TerminatingThreadLocal. Diese Klasse ist neu in JDK11 und existiert nicht in JDK8, daher gibt es in vielen Quellcode-Analysen im Internet keine relevante Beschreibung dieser Klasse. Ich habe mir den Quellcode dieser Klasse angesehen und ihre Funktion sollte darin bestehen, das Problem von ThreadLocal-Speicherlecks zu vermeiden (bei Interesse können Sie den Quellcode überprüfen und mich bei Fehlern bitte korrigieren). Dies ist die offizielle Erklärung:

        void createMap(Thread t, T firstValue) {
            /**
             * 这里是创建一个ThreadLocalMap,以当前调用线程的实例对象为key,初始值为value
             * 然后放入当前线程的Therad.threadLocals属性里面
             */
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }

    remove

        ThreadLocalMap getMap(Thread t) {
            //这里就是直接获取调用线程的成员属性threadlocals
            return t.threadLocals;
        }

    Summary

    In jedem Thread gibt es eine Mitgliedsvariable namens threadLocals, den Typ dieser Variablen ist Hash Map, wobei key die Referenz der von uns definierten ThreadLocal-Variablen ist und value der Wert ist, den wir mit der Set-Methode festgelegt haben. Die lokalen Variablen jedes Threads werden in der Thread-eigenen Speichervariablen threadLocals gespeichert. Wenn der aktuelle Thread nie stirbt, sind diese lokalen Variablen immer vorhanden, sodass es zu einem Speicherüberlauf kommen kann. Denken Sie daher daran, die Methode zum Löschen von ThreadLocal aufzurufen Verwenden Sie lokale Variablen in threadLocals, die dem Thread entsprechen.

    ThreadLokaler SpeicherverlustsetInitialValue的代码

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

    这里我需要补充说明一下TerminatingThreadLocal

    Warum tritt ein Speicherverlust auf?

    ThreadLocalMap verwendet die schwache Referenz von ThreadLocal als Schlüssel. Wenn ein ThreadLocal keine externe starke Referenz hat, um darauf zu verweisen, wird ThreadLocal während der System-GC zwangsläufig recycelt Auf diese Weise werden ThreadLocalMap-Einträge mit Nullschlüsseln in

    angezeigt, und es gibt keine Möglichkeit, auf die Werte dieser Einträge mit Nullschlüsseln zuzugreifen.

    Wenn der aktuelle Thread längere Zeit nicht endet, Die Werte dieser Einträge mit Nullschlüsseln werden immer eine starke Referenzkette sein: Thread Ref -> Thread -> Wert kann niemals recycelt werden, was zu Speicherverlusten führt.

    Tatsächlich wurde diese Situation beim Entwurf von ThreadLocalMap berücksichtigt und einige Schutzmaßnahmen hinzugefügt: Alle Schlüssel im Thread ThreadLocalMap werden beim Abrufen (), Set () und Entfernen () gelöscht. von ThreadLocal sind Nullwerte. Diese passiven vorbeugenden Maßnahmen garantieren jedoch nicht, dass es keine Speicherlecks gibt:

    Die Verwendung von static ThreadLocal verlängert den Lebenszyklus von ThreadLocal, was zu Speicherlecks führen kann# 🎜🎜 #

    Die Zuweisung mit ThreadLocal und das Nichtaufrufen der Methoden get(), set(), remove() führt zu Speicherlecks#🎜 🎜#Warum schwache Referenzen verwenden?

      Da wir alle wissen, dass die Verwendung schwacher Referenzen zu ThreadLocalMap-Speicherlecks führt, warum verwenden Beamte immer noch schwache Referenzen anstelle starker Referenzen? Dies beginnt mit dem Unterschied zwischen der Verwendung schwacher und starker Referenzen:
      • Wenn Sie starke Referenzen verwenden: Wir wissen, dass der Lebenszyklus von ThreadLocalMap im Grunde derselbe ist wie der Lebenszyklus von Thread. Wenn der aktuelle Thread nicht beendet wird, wird ThreadLocalMap niemals von GC recycelt und ThreadLocalMap hat einen starken Wert Verweis auf ThreadLocal, dann ThreadLocal Es wird nicht recycelt. Wenn der Thread-Lebenszyklus lang ist und nicht manuell gelöscht wird, führt dies zu einer KV-Akkumulation, was zu OOM führt. Wenn Sie schwache Referenzen verwenden: Objekte in schwachen Referenzen Sie haben einen kurzen Deklarationszeitraum, da im System während der GC das Objekt recycelt wird, solange eine schwache Referenz gefunden wird, unabhängig davon, ob genügend Heap-Speicherplatz vorhanden ist. Wenn die starke Referenz von ThreadLocal recycelt wird, wird auch die schwache Referenz von ThreadLocalMap recycelt. Wenn kv nicht manuell gelöscht wird, führt dies zu einer Wertakkumulation und führt auch zu OOM

      • Im Vergleich dazu Durch die Verwendung schwacher Referenzen kann zumindest garantiert werden, dass OOM nicht durch die Anhäufung von Kartenschlüsseln verursacht wird und der entsprechende Wert beim nächsten Aufruf über die Methoden „remove“, „get“ und „set“ gelöscht werden kann. Es ist ersichtlich, dass die Hauptursache für Speicherverluste nicht in schwachen Referenzen liegt, sondern dass der Lebenszyklus von ThreadLocalMap genauso lang ist wie der von Thread, was zu einer Anhäufung von Werten führt Wenn ein OOM verursacht wird, können wir das richtige Medikament verschreiben und sicherstellen, dass es nach jedem Gebrauch verwendet wird. Rufen Sie einfach die

        -Methode von ThreadLocal auf, um es zu bereinigen.

    Das obige ist der detaillierte Inhalt vonSo verwenden Sie die Java ThreadLocal-Klasse. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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