ThreadLocal的使用

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

相關文章
相關標籤/搜索