咱們知道,進程是OS分配資源的最小單位,而線程是執行操做的最小單位並不享有資源。ThreadLocal實現了線程數據變量的本地存儲,每一個線程都存儲本身的變量,全部的線程都使用一樣的ThreadLocal<T>對象來存取變量,可是每一個線程在存取時看到的變量值是不一樣的,不會影響到其餘線程的變量,而且值能夠爲null。數組
整個實現架構圖以下:架構
一個線程能夠持有多個ThreadLocal對象,每一個ThreadLocal實例其實很輕量級,只保存hashCounter分配給它的hash值和自身的一個弱引用。存取時只要將values裏的obj數組複製當前方法的局部變量中操做就能夠了。併發
原來的JDK中,table是用map實現的;在1.7源碼中使用了object數組來存放<ThreadLocal,T>,如圖間隔存放了kv;這樣作避開了線程併發的鎖操做,大大加快了存取的速度。另外值得提一點的是,hashCounter每次分配給ThreadLocal對象的hash值都是偶數,這樣取得的index位置來存放ThreadLocal對象,index+1位置存放變量值,十分巧妙。this
ThreadLocal類結構以下,咱們接下來依次分析這些方法:spa
介紹完了整個架構,咱們先不去看values這個靜態內部類,其實它維護了ThreadLocal到變量值的映射,一個hashtable而已,回頭再來看它。線程
先來看一眼完成當前線程變量值的get方法:設計
public T get() { // Optimized for the fast path. Thread currentThread = Thread.currentThread();//獲取到當前線程實例 Values values = values(currentThread);//獲取到當前線程對應的values實例 if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; } } else { values = initializeValues(currentThread);//若是當前線程對應的values爲空,就新建一個 } return (T) values.getAfterMiss(this); }
方法中,根據當前線程實例獲取到values,先來看看若是values爲空會如何?code
若是values爲空則對其初始化,調用initializeValues方法:對象
/** * Creates Values instance for this thread and variable type. */ Values initializeValues(Thread current) { return current.localValues = new Values();//new一個values實例 }
咱們來看一下Values的構造方法:blog
Values() { initializeTable(INITIAL_SIZE);//INITIAL_SIZE爲16 this.size = 0;//table中存儲的鍵值對entry數目 this.tombstones = 0;//廢棄的entry數目 }
經過initializeTable方法來建立一個object數組,容量爲32,mask值爲0x1F。
private void initializeTable(int capacity) { this.table = new Object[capacity * 2];//經過給定的初始化容量建立table,一個obj數組 this.mask = table.length - 1;//以前capacity規定必須爲2的冪,這裏length默認爲31, this.clean = 0; this.maximumLoad = capacity * 2 / 3; // 2/3 最大負載因子 }
咱們從新回到get方法中,方法最後返回getAfterMiss(this),該方法將當前ThreadLocal傳入,並返回initialValue()定義的值,這個方法是能夠自定義重寫的。
若是values不爲空,咱們將副本table複製到當前方法變量中進行操做,因爲每一個ThreadLocal對象都有固定的hash值,因此不存在線程併發的問題。
ThreadLocal中其餘的操做方法也是這樣。操做完成後,咱們須要與Values中的數組交互,這裏就調用了put方法:
/** * Sets entry for given ThreadLocal to given value, creating an * entry if necessary. */ void put(ThreadLocal<?> key, Object value) { cleanUp();//先清理了廢棄的元素 // Keep track of first tombstone. That's where we want to go back // and add an entry if necessary. int firstTombstone = -1; for (int index = key.hash & mask;; index = next(index)) { Object k = table[index]; if (k == key.reference) { // Replace existing entry. table[index + 1] = value; return; } if (k == null) { if (firstTombstone == -1) { // Fill in null slot. table[index] = key.reference; table[index + 1] = value; size++; return; } // Go back and replace first tombstone. table[firstTombstone] = key.reference; table[firstTombstone + 1] = value; tombstones--; size++; return; } // Remember first tombstone. if (firstTombstone == -1 && k == TOMBSTONE) { firstTombstone = index; } } }
每次操做object數組,都要先清理一下廢棄的元素。而後再進行元素存放。
總結
ThreadLocal與values的組合設計實現了多個線程存儲本地變量而又互不干擾的功能,更使人叫絕的是經過固定hash值分配的方式,避開了鎖操做。關於Values內部的object數組的維護比較複雜,之後有機會再來研究補充。