ThreadLocal的特色是每個線程對應的對象都只與當前線程有關,當在全局聲明一個使用了ThreadLocal的對象時,在每一個線程調用它時,都會有一個本身線程對應的拷貝,而賦值則是由java
def initialValue() 或set方法來完成的安全
咱們要實現了一個Protocol層,它內部會保留一個socket鏈接數據結構
class ThreadLocalProtocol { val protocol = new ThreadLocal[ZTMultiplexedProtocol]() { override def initialValue(): ZTMultiplexedProtocol = { val zmp = ZLineStatusClient.getZmp zmp.openTTransport() zmp } } def getProtocol(): ZTMultiplexedProtocol = { protocol.get() } }
咱們在主線程進行多線程
lazy val threadLocalProtocol = new ThreadLocalProtocol
這裏用不用lazy應該是同樣的,由於ThreadLocal.get()調用時,纔會延遲加載對象,這裏是ZTMultiplexedProtocol,咱們來看看jdk源碼socket
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @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) return (T)e.value; } return setInitialValue(); }
若是map裏面已經有對象了,就返回其值,這裏進行了一次強制類型轉換,不然調用setInitialValue(),咱們再來看看ide
/** * Variant of set() to establish initialValue. Used instead * of set() in case user has overridden the set() method. * * @return the initial value */ 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; }
setInitialValue()方法其實也是對map的操做,那麼這個名字叫map的東西究竟是什麼呢?咱們先來看看getMap(t)是什麼東西this
/** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
Thread.threadLocals返回的就是ThreadLocalMap,因爲Thread.currentThread()方法是native的,不得而知他是怎麼初始化threadLocals變量的,根據map.set方法的代碼
線程
/** * Set the value associated with key. * * @param key the thread local object * @param value the value to be set */ private void set(ThreadLocal key, Object value) { // We don't use a fast path as with get() because it is at // least as common to use set() to create new entries as // it is to replace existing ones, in which case, a fast // path would fail more often than not. Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal k = e.get(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
咱們所聲明的threadLocalProtocol對象,在每一個線程set值時,用的是該線程的ThreadLocalMap,這個map能夠維護多個不一樣的ThreadLocal對象,也就是一對多,好比這樣:scala
lazy val threadLocalProtocol1 = new ThreadLocalProtocol lazy val threadLocalProtocol2 = new ThreadLocalProtocol lazy val threadLocalProtocol3 = new ThreadLocalProtocol
在每一個線程裏面調用threadLocalProtocol.getProtocol()觸發ThreadLocal對象內部的get()方法,而後Thread.currentThread().threadLocals對象就維護了3個ZTMultiplexedProtocol
,這裏就不細說ThreadLocalMap的數據結構了,它內部採用了WeakReference,這點多是出於效率來考慮的。code
總之,在你多線程中調用set(),get()方法是,他們都是對於當前線程而言的,和其餘線程沒有關係了
這個功能在多線程中進行相似創建socketConnection時,至關有用
可是咱們還有疑慮,何不在每一個線程中直接 new 一個ZTMultiplexedProtocol呢?
試想一下,咱們的代碼是否是有那麼簡單的邏輯,能夠在run()方法中直接使用ZTMultiplexedProtocol,或者只有在run()中才須要ZTMultiplexedProtocol呢,這一點很是重要,加入ZTMultiplexedProtocol的對象須要被傳來傳去,是否是咱們要在每一個方法上都加上參數(zmp:ZTMultiplexedProtocol)呢,這個就好不麻煩了,若是有如下一個解決辦法,豈不是簡潔明朗多了:
Object G{ private class ThreadLocalProtocol { val protocol = new ThreadLocal[ZTMultiplexedProtocol]() { override def initialValue(): ZTMultiplexedProtocol = { val zmp = ZLineStatusClient.getZmp zmp.openTTransport() zmp } } def getProtocol(): ZTMultiplexedProtocol = { protocol.get() } } val hreadLocalProtocol = new ThreadLocalProtocol }
每一個須要這個ZTMultiplexedProtocol的地方,咱們只須要這樣得到
G.hreadLocalProtocol.getProtocol()
這樣,不管在哪裏均可以經過一個全局的對象直接得到ZTMultiplexedProtocol了,避免了代碼的冗長,方法的不健康(好比,它完成的任務只是依賴了ZTMultiplexedProtocol,甚至均可能不會使用,咱們也須要在它的父接口中聲明這個參數)
ThreadLocal用在鏈接池技術中也何嘗不可,只要咱們保證線程數不會瘋漲,而是控制在一個線程池中的,咱們用ThreadLocal這樣的方式也是安全的
本文出自 「沐浴心情」 博客,請務必保留此出處http://lj3331.blog.51cto.com/5679179/1353702