ThreadLocal原理


1.  ThreadLocal是什么

ThreadLocal提供線程局部變量。這些變量與普通的變量不同之處在於,每個訪問這種變量的線程(通過它的get或set方法)都有自己的、獨立初始化的變量副本。

ThreadLocal實例通常是希望將狀態關聯到一個線程的類的私有靜態字段(比如,user ID 或者 Transaction ID 等等)。

畫外音:這段話表達了三個意思

  1. ThreadLocal是一種變量類型,我們稱之為“線程局部變量”
  2. 每個線程訪問這種變量的時候都會創建該變量的副本,這個變量副本為線程私有
  3. ThreadLocal類型的變量一般用private static加以修飾

例如,下面的例子中這個類為每個線程生成唯一標識。一個線程的id是它第一次調用ThreadId.get()方法指定的。

package com.cjs.example;

import java.util.concurrent.atomic.AtomicInteger;

public class ThreadId {

    private static final AtomicInteger nextId = new AtomicInteger(0);

    private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return nextId.getAndIncrement();
        }
    };

    public static int get() {
        return threadId.get();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getId());
                }
            }).start();
        }
    }
}

2.  ThreadLocal主要操作

/**
 * 返回當前線程對ThreadLocal變量的“初始值”
 * 這個方法將在線程第一次訪問變量(通過調用get方法)時被調用,如果之前已經調用過了就不會再調了
 *
 * @return the initial value for this thread-local
 */
protected T initialValue() {
    return null;
}

/**
 * 設置當前線程的ThreadLocal變量的副本為指定的值
 *
 * @param value the value to be stored in the current thread's copy of this thread-local.
 */
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

/**
 * 返回當前線程的ThreadLocal變量副本的值
 *
 * @return the current thread's value of this thread-local
 */
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();
}

/**
 * 刪除當前線程的ThreadLocal變量副本的值
 */
public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

3.  閱讀源碼

3.1.  set方法

可以看到,ThreadLocalMap底層是一個數組,數組中元素類型是Entry類型

set操作是向當前線程的ThreadLocal.ThreadLocalMap類型的成員變量threadLocals中設置值,key是this,value是我們指定的值

注意,這里傳的this代表的是那個ThreadLocal類型的變量(或者說叫對象)

也就是說,每個線程都維護了一個ThreadLocal.ThreadLocalMap類型的對象,而set操作其實就是以ThreadLocal變量為key,以我們指定的值為value,最后將這個鍵值對封裝成Entry對象放到該線程的ThreadLocal.ThreadLocalMap對象中。每個ThreadLocal變量在該線程中都是ThreadLocal.ThreadLocalMap對象中的一個Entry。既然每個ThreadLocal變量都對應ThreadLocal.ThreadLocalMap中的一個元素,那么就可以對這些元素進行讀寫刪除操作。

3.2.  get方法

get()方法就是從當前線程的ThreadLocal.ThreadLocalMap對象中取出對應的ThreadLocal變量所對應的值

同理,remove()方法就是清除這個值

用圖形表示的話,大概是這樣的:

或者是這樣的:

4.  ThreadLocal使用場景

  1. 在線程生命周期內傳值

最后,一切都歸功於ThreadLocalMap

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM