Heim  >  Artikel  >  Java  >  Die Verwendung von ThreadLocal im Java-Multithreading

Die Verwendung von ThreadLocal im Java-Multithreading

不言
不言nach vorne
2018-10-17 16:21:532447Durchsuche

Der Inhalt dieses Artikels befasst sich mit der Verwendung von ThreadLocal im Java-Multithreading. Ich hoffe, dass er für Freunde hilfreich ist.

In einer Multithread-Umgebung muss die Thread-Synchronisierung durchgeführt werden, wenn auf nicht-threadsichere Variablen zugegriffen wird, z. B. wenn die synchronized-Methode verwendet wird, um auf HashMap-Instanzen zuzugreifen. Allerdings verringert der synchrone Zugriff die Parallelität und beeinträchtigt die Systemleistung. Zu diesem Zeitpunkt können Sie Platz gegen Zeit eintauschen. Wenn wir jedem Thread eine unabhängige Variable zuweisen, können Sie nicht-thread-sichere Variablen asynchron verwenden. Wir nennen solche Variablen Thread-lokale Variablen.

Wie der Name schon sagt, bedeuten lokale Thread-Variablen, dass jeder Thread seine eigene unabhängige Kopie der Variablen hat, auf die andere Threads nicht wie gewöhnliche lokale Variablen zugreifen können. Java stellt keine lokalen Thread-Variablen auf Sprachebene bereit, stellt jedoch die Funktion lokaler Thread-Variablen in der Klassenbibliothek bereit, die diesmal die Protagonistenklasse ThreadLocal ist.

Verwendung von ThreadLocal

Die Verwendung von ThreadLocal im Java-Multithreading

Die Java8-Version von ThreadLocal hat 4, wie im gezeigt Bild oben Es gibt zwei öffentliche Methoden und eine geschützte Methode. Die erste Methode wird verwendet, um den Anfangswert zurückzugeben, der standardmäßig null ist. Die zweite statische Methode withInitial(Supplier erweitert S> Lieferant) wurde in der Java8-Version neu hinzugefügt, und die nächsten drei Instanzmethoden sind sehr einfach.

Wenn Sie ThreadLocal verwenden und den Anfangswert festlegen möchten, müssen Sie die ThreadLocal-Klasse erben und die geschützte T initialValue()-Methode überschreiben. Beispiel:

ThreadLocal<integer> threadLocal = new ThreadLocal<integer>() {
    @Override
    protected Integer initialValue() {
        return 0;
    }
};</integer></integer>

kann in der Java8-Version verwendet werden. Die neu hinzugefügte statische Methode withInitial(Supplier erweitert S> Lieferant) ist sehr praktisch, um den Anfangswert festzulegen, zum Beispiel:

ThreadLocal<integer> threadLocal = ThreadLocal.withInitial(() -> 0);

System.out.println(threadLocal.get());
threadLocal.set(16);
System.out.println(threadLocal.get());
threadLocal.remove();
System.out.println(threadLocal.get());

// 同一个线程的输出
0
16
0
Process finished with exit code 0</integer>

Das Prinzip von ThreadLocal

Dann ist ThreadLocal Wie implementiert man die Funktion lokaler Thread-Variablen? Tatsächlich ist das Grundprinzip von ThreadLocal nicht sehr kompliziert. ThreadLocal definiert intern eine statische Klasse ThreadLocalMap. Der Wert von ThreadLocalMap ist der von ThreadLocal gespeicherte Wert. Schauen wir uns einen Teil des Quellcodes von ThreadLocal an:

    // ThreadLocal的set方法
    public void set(T value) {
        // 获取当前线程对象
        Thread t = Thread.currentThread();
        // 获取Map
        ThreadLocalMap map = getMap(t);
        if (map != null)
            // 设置值
            map.set(this, value);
        else
            // 初始化Map
            createMap(t, value);
    }
    
    // ThreadLocal的createMap方法
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    
    // Thread类定义的实例域
    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

Es ist ersichtlich, dass die Kernimplementierung von ThreadLocal die Implementierung von ThreadLocalMap ist, die intern eine Entry-Klasse zum Speichern von Daten deklariert:

static class Entry extends WeakReference<threadlocal>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal> k, Object v) {
        super(k);
        value = v;
    }
}</threadlocal>

ThreadLocalMap-Implementierung Es gibt Ähnlichkeiten mit der Implementierung von HashMap. Beispielsweise werden Arrays auch zum Speichern von Daten und zur automatischen Erweiterung verwendet. Der Unterschied besteht darin, dass der Hash-Algorithmus und die Verarbeitung nach der Hash-Kollision unterschiedlich sind.

        // ThreadLocalMap的set方法
        private void set(ThreadLocal> key, Object value) {

            Entry[] tab = table;
            int len = tab.length;
            // 计算在Entry[]中的索引,每个ThreadLocal对象都有一个hash值threadLocalHashCode,每初始化一个ThreadLocal对象,hash值就增加一个固定的大小0x61c88647
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 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);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
        
        private static int nextIndex(int i, int len) {
            return ((i + 1 <p>Sie können sehen, dass ThreadLocalMap das Array Entry[] als Ring behandelt. Wenn der Index ausgehend von der berechneten Indexposition bereits Daten enthält, wird beurteilt, ob der Schlüssel derselbe ist, und wenn er identisch ist, wird der Wert aktualisiert. Andernfalls warten Sie einfach, bis Sie eine leere Position gefunden haben, und geben Sie dort den Wert ein. Das Gleiche gilt, wenn Sie ausgehend von der berechneten Indexposition prüfen, ob die Schlüssel nacheinander gleich sind. Wenn es viele Hash-Kollisionen gibt, ist die Leistung möglicherweise nicht sehr gut. </p><p><strong>ThreadLocal application</strong><code><code><br></code></code></p><p><code><span style="font-family: Microsoft YaHei, Microsoft YaHei; "> ThreadLocal wird häufig verwendet. Das Spring-Framework, mit dem Java-Ingenieure sehr vertraut sind, verwendet ThreadLocal, um nicht threadsichere zustandsbehaftete Objekte zu kapseln, sodass wir die meisten Beans als Singleton-Bereich deklarieren können. Wenn wir Multithread-Code schreiben, können wir auch darüber nachdenken, ob es besser ist, synchron auf nicht-threadsichere zustandsbehaftete Objekte zuzugreifen oder ob es besser ist, ThreadLocal zum Kapseln nicht-threadsicherer zustandsbehafteter Objekte zu verwenden. <code><span style="font-family: 微软雅黑, Microsoft YaHei;">ThreadLocal的应用是非常广的,比如Java工程师非常熟悉的Spring框架中就使用了ThreadLocal来把非线程安全的状态性对象封装起来,所以我们可以把绝大部分的Bean声明为singleton作用域。我们在编写多线程代码时也可以想想是用同步的方式访问非线程安全的状态性对象比较好,还是使用ThreadLocal把非线程安全的状态性对象封装起来更好。</span><br></code></span></code></p><p class="comments-box-content"><br></p>

Das obige ist der detaillierte Inhalt vonDie Verwendung von ThreadLocal im Java-Multithreading. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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