ThreadLocal(一):Thread 、ThreadLocal、ThreadLocalMap

 1、ThreadLocalMap是ThreadLocal的內部類、Thread持有ThreadLocalMap的引用

Entry類繼承了WeakReference<ThreadLocal<?>>,即每一個Entry對象都有一個ThreadLocal的弱引用(做爲key),這是爲了防止內存泄露。html

key變爲一個不可達的對象(null),這個Entry就能夠被GC了。算法

 1  static class ThreadLocalMap {
 2 
 3         /**
 4          * The entries in this hash map extend WeakReference, using
 5          * its main ref field as the key (which is always a
 6          * ThreadLocal object).  Note that null keys (i.e. entry.get()
 7          * == null) mean that the key is no longer referenced, so the
 8          * entry can be expunged from table.  Such entries are referred to
 9          * as "stale entries" in the code that follows.
10          */
11         static class Entry extends WeakReference<ThreadLocal> {
12             /** The value associated with this ThreadLocal. */
13             Object value;
14 
15             Entry(ThreadLocal k, Object v) {
16                 super(k);
17                 value = v;
18             }
19         }

 

1.ThreadLocalMap成員變量分析,放入第一個元素時,才生成Entry數組實例

 /*** The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;

        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;

        /**
         * The number of entries in the table.
         */
        private int size = 0;

        /**
         * The next size value at which to resize.
         */
        private int threshold; // Default to 0

        /**
         * Set the resize threshold to maintain at worst a 2/3 load factor.
         */
        private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }

        /**
         * Increment i modulo len.
         */
        private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
        }

        /**
         * Decrement i modulo len.
         */
        private static int prevIndex(int i, int len) {
            return ((i - 1 >= 0) ? i - 1 : len - 1);
        }

        /**
         * Construct a new map initially containing (firstKey, firstValue).
         * ThreadLocalMaps are constructed lazily, so we only create
         * one when we have at least one entry to put in it.  
         */
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

 ThreadLocalMap構造函數的第一個參數就是該ThreadLocal實例(this),第二個參數就是要保存的線程本地變量。數據庫

構造函數首先建立一個長度爲16的Entry數組,而後計算出firstKey對應的哈希值,而後存儲到table中,並設置size和threshold。數組

注意一個細節,計算hash的時候裏面採用了hashCode & (size - 1)的算法,這至關於取模運算hashCode % size的一個更高效的實現(和安全

HashMap中的思路相同)。正是由於這種算法,咱們要求size必須是2的指數,由於這可使得hash發生衝突的次數減少。多線程

 

二、Thread持有兩個ThreadLocalMap的引用,一個用於保存當前線程的局部變量,另外一個保存父線程的局部變量

1     /* ThreadLocal values pertaining to this thread. This map is maintained
2      * by the ThreadLocal class. */
3     ThreadLocal.ThreadLocalMap threadLocals = null;
4 
5     /*
6      * InheritableThreadLocal values pertaining to this thread. This map is
7      * maintained by the InheritableThreadLocal class.  
8      */ 
9     ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

 

 

2、ThreadLocal的set()方法

 1    public void set(T value) {
 2         Thread t = Thread.currentThread();
 3         ThreadLocalMap map = getMap(t);
 4         if (map != null)
 5             map.set(this, value);                        //以ThreadLocal爲鍵
 6         else
 7             createMap(t, value);
 8     }
 9    void createMap(Thread t, T firstValue) {
10         t.threadLocals = new ThreadLocalMap(this, firstValue);     //以ThreadLocal爲鍵
11 }

 

ThreadLocal的get()方法併發

 1    public T get() {
 2         Thread t = Thread.currentThread();  //獲得當前線程
 3         ThreadLocalMap map = getMap(t);     //獲得當前線程相關的ThreadLocalMap
 4         if (map != null) {
 5             ThreadLocalMap.Entry e = map.getEntry(this);
 6             if (e != null)
 7                 return (T)e.value;
 8         }
 9         return setInitialValue();
10     }
11 
12    ThreadLocalMap getMap(Thread t) {
13         return t.threadLocals;
14     }

 

ThreadLocal在Hibernate中的應用less

 1     private static final ThreadLocal threadSession = new ThreadLocal();
 2 
 3     public static Session getSession() throws InfrastructureException {
 4         Session s = (Session) threadSession.get();
 5         try {
 6             if (s == null) {
 7                 s = getSessionFactory().openSession();
 8                 threadSession.set(s);
 9             }
10         } catch (HibernateException ex) {
11             throw new InfrastructureException(ex);
12         }
13         return s;
14     }

 

3、總結

        咱們在多線程的開發中,常常會考慮到的策略是對一些須要公開訪問的屬性經過設置同步的方式來訪問。這樣每次能保證只有一個線程訪問它,不會有衝突。函數

可是這樣作的結果會使得性能和對高併發的支持不夠。在某些狀況下,若是咱們不必定非要對一個變量共享不可,而是給每一個線程一個這樣的資源副本,讓他們可高併發

以獨立都各自跑各自的,這樣不是能夠大幅度的提升並行度和性能了嗎?

        還有的狀況是有的數據自己不是線程安全的,或者說它只能被一個線程使用,不能被其餘線程同時使用。若是等一個線程使用完了再給另一個線程使用就

根本不現實。這樣的狀況下,咱們也能夠考慮用ThreadLocal。一個典型的狀況就是咱們鏈接數據庫的時候一般會用到鏈接池。而對數據庫的鏈接不能有多個線程

共享訪問。這個時候就須要使用ThreadLocal了。

 1 private static ThreadLocal<Connection> connectionHolder = 
 2     new ThreadLocal<Connection>() {
 3         public Connection initialValue() {
 4             return DriverManager.getConnection(DB_URL);
 5         }
 6     };
 7 
 8 
 9 pubic static Connection getConnection() {
10     return connectionHolder.get();
11 }

 

 4、ThreadLocal使用總結

ThreadLocal對象建議使用static修飾。這個變量是針對一個線程內全部操做共有的,

因此設置爲靜態變量,全部此類實例共享此靜態變量 ,也就是說在類第一次被使用時裝載,只分配一塊存儲空間 

 

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).

 

Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the 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).

 

參考:

深刻理解 Java 之 ThreadLocal 工做原理

相關文章
相關標籤/搜索