ThreadLocal

每天學(xué)習(xí)一點(diǎn)點(diǎn)

ThreadLocal在android中屬于一個(gè)常用的類,比如的Android最重要的Handler消息機(jī)制里面的Looper存儲(chǔ)也是采用ThreadLocal,開源框架EventBus存儲(chǔ)當(dāng)前線程下的發(fā)送事件隊(duì)列狀態(tài)也是采用ThreadLocal。

ThreadLocal到底能做什么呢?原理又是什么呢?

點(diǎn)進(jìn)ThreadLocal的類文件看一下,類上寫的注釋。

This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.  {@code ThreadLocal} instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).
 *
 * <p>For example, the class below generates unique identifiers local to each
 * thread.
 * A thread's id is assigned the first time it invokes {@code ThreadId.get()}
 * and remains unchanged on subsequent calls.
 * <pre>
 * import java.util.concurrent.atomic.AtomicInteger;
 *
 * public class ThreadId {
 *     // Atomic integer containing the next thread ID to be assigned
 *     private static final AtomicInteger nextId = new AtomicInteger(0);
 *
 *     // Thread local variable containing each thread's ID
 *     private static final ThreadLocal&lt;Integer&gt; threadId =
 *         new ThreadLocal&lt;Integer&gt;() {
 *             &#64;Override protected Integer initialValue() {
 *                 return nextId.getAndIncrement();
 *         }
 *     };
 *
 *     // Returns the current thread's unique ID, assigning it if necessary
 *     public static int get() {
 *         return threadId.get();
 *     }
 * }
 * </pre>
 * <p>Each thread holds an implicit reference to its copy of a thread-local
 * variable as long as the thread is alive and the {@code ThreadLocal}
 * instance is accessible; after a thread goes away, all of its copies of
 * thread-local instances are subject to garbage collection (unless other
 * references to these copies exist).

大體意思就是ThreadLocal 實(shí)現(xiàn)一個(gè)線程本地的存儲(chǔ)。每個(gè)線程都有自己的局部變量。所有線程都共享一個(gè)ThreadLocal對(duì)象,但是每個(gè)線程在訪問這些變量的時(shí)候能得到不同的值,每個(gè)線程可以更改這些變量并且不會(huì)影響其他的線程,并且支持null值。

我們看下ThreadLocal的方法有哪些?
  • void set(Object value)設(shè)置當(dāng)前線程的線程局部變量的值。
  • public Object get()該方法返回當(dāng)前線程所對(duì)應(yīng)的線程局部變量。
  • public void remove()將當(dāng)前線程局部變量的值刪除,目的是為了減少內(nèi)存的占用,該方法是JDK 5.0新增的方法。需要指出的是,當(dāng)線程結(jié)束后,對(duì)應(yīng)該線程的局部變量將自動(dòng)被垃圾回收,所以顯式調(diào)用該方法清除線程的局部變量并不是必須的操作,但它可以加快內(nèi)存回收的速度。
  • protected Object initialValue()返回該線程局部變量的初始值,該方法是一個(gè)protected的方法,顯然是為了讓子類覆蓋而設(shè)計(jì)的。這個(gè)方法是一個(gè)延遲調(diào)用方法,在線程第1次調(diào)用get()或set(Object)時(shí)才執(zhí)行,并且僅執(zhí)行1次。ThreadLocal中的缺省實(shí)現(xiàn)直接返回一個(gè)null。
我們使用一下ThreadLocal

創(chuàng)建一個(gè)ThreadLocal,存入一段文本,看在兩個(gè)線程取出值是多少。

 local = new ThreadLocal<>();
        local.set("存入測(cè)試");
        Thread t1 = new Thread(new Runnable() {


            @Override
            public void run() {

                Log.e(TAG, "t1 get(): " + local.get());
                local.set("t1");
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG, "t2 get() " + local.get());
            }
        });

        t1.start();
        t2.start();
     Log.e(TAG, "main get(): "+local.get() );
結(jié)果

我們發(fā)現(xiàn)在t1 t2中取出值都是null,那么我們?cè)趖1 t2中存儲(chǔ)再取出驗(yàn)證下

 local = new ThreadLocal<>();
        local.set("存入測(cè)試");
        Thread t1 = new Thread(new Runnable() {


            @Override
            public void run() {
                local.set("t1");
                Log.e(TAG, "t1 get(): " + local.get());

            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                local.set("t2");
                Log.e(TAG, "t2 get() " + local.get());
            }
        });
        t1.start();
        t2.start();
        Log.e(TAG, "main get(): "+local.get() );
image.png

獲取大家好奇,怎么就在拿不到local的值呢,我們上面說到ThreadLocal是每個(gè)線程可以訪問自己內(nèi)部的副本變量。

我們看下ThreadLocal是怎么實(shí)現(xiàn)這種神奇機(jī)制的的?

先看下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);
    }

我們不去關(guān)注細(xì)節(jié),大家有時(shí)間可以仔細(xì)看下源碼,我們獲取了一個(gè)map,進(jìn)行了判null處理,將值存進(jìn)去。如果是null就去創(chuàng)建map并將值賦進(jìn)去??梢岳斫鉃閗ey就是當(dāng)前的線程。
再看一下get()方法。

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

根據(jù)線程,取出一個(gè)map,并根據(jù)類型取出一個(gè)Entry,根據(jù)泛型強(qiáng)轉(zhuǎn),并返回,如果沒有的話,我們看到一個(gè)setInitialValue追一下

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

protected T initialValue() {
        return null;
    }

可以看到如果沒查到值initialValue,會(huì)默認(rèn)返回一個(gè)null。
其實(shí)ThreadLocalMap的源碼大家可以看一下,因?yàn)榇a量較多,我就不粘出來了。我們剛看到的Entry其實(shí)是一個(gè)WeakReference。

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

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

最后看下remove()方法

         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

其實(shí)也就是根據(jù)當(dāng)前線程判斷,刪除this,這里的this指向的就是ThreadLocal自己。
其實(shí)還有很多細(xì)節(jié),大家可以研究下源碼的時(shí)候看一下,想一下
這篇文章寫的不錯(cuò),大家可以看看。
https://www.cnblogs.com/xzwblog/p/7227509.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容