ThreadLocal的工作原理

weixin_33850890發表於2017-05-24

概述

ThreadLocal是一個用來儲存執行緒內部資料的類,什麼意思呢?ThreadLocal 重點突出的是Local,也就是儲存的這個資料只有對應的執行緒才能訪問。其他的執行緒是訪問不到。哪個執行緒存,就只能哪個執行緒取。那是怎麼做到的呢?

  • 以ThreadLocal物件為鍵,任意物件為值作為儲存結構;
  • 然後把這個key-value 結構存入到ThreadLocalMap的物件中;
  • 而實際上每個執行緒物件都有一個同樣型別的變數指向這個ThreadLocalMap物件,且是唯一的。
  • 存取都是對這個map操作,這樣就實現了執行緒內部存取的策略。
1319564-b3c81e60a464cf5b.png
ThreadLocal工作類圖.png

原始碼分析(JDK5)

從概述中我們能看出來,真正實現儲存資料的是這個ThreadLocalMap,那這裡先不分析其的實現,暫且把他先理解成HashMap,便於分析。

  1. 我們從set(T value)開始分析

     public void set(T value) {
         Thread t = Thread.currentThread(); //1、獲取此方法的呼叫者執行緒
         ThreadLocalMap map = getMap(t);    //2.獲取與這個執行緒繫結的ThreadLocalMap物件
         if (map != null)                   //3.判斷這個物件是否為空
             map.set(this, value);          //4.不為空,就儲存這個鍵值對
         else
             createMap(t, value);           //5.為空,就去建立這個map物件並儲存這個鍵值對
     }
    

    原始碼中可以看到對這個map物件進行了唯一性的操作,只有為空的時候才會去建立。

    那再來去看一下這個createMap(Thread t, T firstValue)getMap(Thread t)

     void createMap(Thread t, T firstValue) {
         t.threadLocals = new ThreadLocalMap(this, firstValue);
     }
    

    把建立的這個物件賦值給了Thread物件的一個變數t.threadLocals

     ThreadLocalMap getMap(Thread t) {
         return t.threadLocals;
     }
    

    實際上就是獲取這個執行緒儲存的map物件。再來看一眼這個threadLocals在Thread中的定義

    1319564-3be2b122193f1477.png
    Thread中宣告瞭ThreadLocalMap.png
  2. 取值 get()

     public T get() {
         Thread t = Thread.currentThread();
         ThreadLocalMap map = getMap(t);
         if (map != null) { // 不為空,就從map中取出這個threadLocal為key的entry物件
             ThreadLocalMap.Entry e = map.getEntry(this);
             if (e != null) {
                 @SuppressWarnings("unchecked")
                 T result = (T)e.value;
                 return result;
             }
         }
         return setInitialValue(); //當map為null時候,去初始化這個Map並返回null
     }
    

    如果你對Map瞭解,那麼Entry的獲取就不難了。這裡姑且先這麼認為ThreadLocalMap就是hashmap以便於理解。

  3. 移除 remove()

     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread()); //1.獲得map
         if (m != null)           //2.map不為空,從map中移除
             m.remove(this);
     }
    

ThreadLocal工作原理可以簡單的概況為:
每個執行緒都會唯一繫結一個ThreadLocalMap的物件,用TheadLocal作為這個map的鍵,來存取值。

用法

public class MyClass {

    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

    public static void main(String args[]) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                threadLocal.set(1);
                new A().get();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                threadLocal.set(10);
                new A().get();
            }
        }).start();

    }

    static class A {
        public void get() {
            int data = threadLocal.get();
            System.out.println("A from" + Thread.currentThread().getName() + "  get data:" + data);
        }
    }

}
1319564-b17bb3672b200c17.png
執行結果.png

總結

  • ThreadLocal不是用來解決物件共享訪問問題的,而主要是提供了執行緒保持物件的方法和避免引數傳遞的方便的物件訪問方式。

  • 使用場景,在Android中的Looper的儲存就是這個ThreadLocal的運用。

      // sThreadLocal.get() will return null unless you've called prepare().
      static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
      private static void prepare(boolean quitAllowed) {
          if (sThreadLocal.get() != null) {
              throw new RuntimeException("Only one Looper may be created per thread");
          }
          sThreadLocal.set(new Looper(quitAllowed));
      }  
    
     /**
       * Return the Looper object associated with the current thread.  Returns
       * null if the calling thread is not associated with a Looper.
       */
      public static Looper myLooper() {
          return sThreadLocal.get();
      }
    

    將Looper這個物件儲存在對應的執行緒中,這樣保證了一個執行緒對應一個Looper。

  • 那有關ThreadLocalMap內部是如何來存取資料的,關後更......

相關文章