ThreadLocal源碼分析

ThreadLocal和線程同步機(jī)制相比:都是為了解決多線程中相同變量的訪問沖突問題呼伸。
  在同步機(jī)制中恐锦,通過對象的鎖機(jī)制保證同一時(shí)間只有一個(gè)線程訪問變量椿浓。這時(shí)該變量是多個(gè)線程共享的凑兰。
  ThreadLocal會為每一個(gè)線程提供一個(gè)獨(dú)立的變量副本底洗,從而隔離了多個(gè)線程對數(shù)據(jù)的訪問沖突款筑。ThreadLocal提供了線程安全的共享相同名稱的不同對象智蝠。
  在一個(gè)村中有100戶人家,當(dāng)大家要使用村里唯一的一臺拖拉機(jī)時(shí)奈梳,需要使用同步機(jī)制杈湾。當(dāng)每家騎自家的自行車時(shí),使用ThreadLocal攘须,雖然大家騎的都是自行車漆撞,但是是不同的自行車實(shí)例。

Paste_Image.png
Paste_Image.png

package com.hfbank.biz.service;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicInteger;

/**

  • Implements a thread-local storage, that is, a variable for which each thread

  • has its own value. All threads share the same {@code ThreadLocal} object,

  • but each sees a different value when accessing it, and changes made by one

  • thread do not affect the other threads. The implementation supports

  • {@code null} values.

  • @see java.lang.Thread

  • @author Bob Lee

  • jxy 為了更好的使用于宙,1.5增加了泛型的支持浮驳,可以看出為了能夠更好更簡單的被使用,不放過需要優(yōu)化的任何地方

  • Values中為什么不把:private Object[] table;設(shè)置為泛型捞魁?

  • 一個(gè)ThreadLocal只能保持一個(gè)Object至会,如果想保持多個(gè)應(yīng)如何處理?
    */
    public class ThreadLocal<T> {

    /* Thanks to Josh Bloch and Doug Lea for code reviews and impl advice. */
    // jxy 已經(jīng)多次看到此類注釋谱俭,代碼如人生

    /**

    • Creates a new thread-local variable.
      */
      public ThreadLocal() {}

    /**

    • Returns the value of this variable for the current thread. If an entry

    • doesn't yet exist for this variable on this thread, this method will

    • create an entry, populating the value with the result of

    • {@link #initialValue()}.

    • @return the current value of the variable for the calling thread.
      /
      @SuppressWarnings("unchecked")
      public T get() {
      // Optimized for the fast path.
      Thread currentThread = Thread.currentThread();
      /
      *

      • jxy values是需要和Thread類結(jié)合使用的奉件,并且是一個(gè)類似map的存儲類
      • 在Thread類中有: java.lang.ThreadLocal.Values localValues;
      • localValues在Thread類中只是定義為包內(nèi)可見宵蛀,但沒有任何操作,就是為了給ThreadLocal類使用
      • Thread類的實(shí)例在運(yùn)行時(shí)县貌,每個(gè)線程都是不同的實(shí)例术陶,這樣localValues也就不會相互干擾
      • 這樣兩個(gè)類配合實(shí)現(xiàn)不同線程獲取不同實(shí)例的需求。
      • 如果不和Thread類配合能否實(shí)現(xiàn)現(xiàn)有功能煤痕?
      • values為什么做成可以存儲多個(gè)key-value對梧宫?
        */
        Values values = values(currentThread);
        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);
        }

      return (T) values.getAfterMiss(this);
      }

    /**

    • Provides the initial value of this variable for the current thread.
    • The default implementation returns {@code null}.
    • @return the initial value of the variable.
    • jxy 該函數(shù)是protected類型的,很顯然是建議在子類重載該函數(shù)的摆碉,所以通常該函數(shù)都會以匿名內(nèi)部類的形式被重載塘匣,以指定初始值
      */
      protected T initialValue() {
      return null;
      }

    /**

    • Sets the value of this variable for the current thread. If set to
    • {@code null}, the value will be set to null and the underlying entry will
    • still be present.
    • @param value the new value of the variable for the caller thread.
      */
      public void set(T value) {
      Thread currentThread = Thread.currentThread();
      Values values = values(currentThread);
      if (values == null) {
      values = initializeValues(currentThread);
      }
      values.put(this, value);
      }

    /**

    • Removes the entry for this variable in the current thread. If this call
    • is followed by a {@link #get()} before a {@link #set},
    • {@code #get()} will call {@link #initialValue()} and create a new
    • entry with the resulting value.
    • @since 1.5
      */
      public void remove() {
      Thread currentThread = Thread.currentThread();
      Values values = values(currentThread);
      if (values != null) {
      values.remove(this);
      }
      }

    /**

    • Creates Values instance for this thread and variable type.
      */
      Values initializeValues(Thread current) {
      return current.localValues = new Values();
      }

    /**

    • Gets Values instance for this thread and variable type.
      */
      Values values(Thread current) {
      return current.localValues;
      }

    /** Weak reference to this thread local instance. */
    // jxy 此處使用WeakReference可以使得當(dāng)ThreadLocal可以被回收,而不會因?yàn)樵趘alues中保存有引用而無法回收
    private final Reference<ThreadLocal<T>> reference = new WeakReference<ThreadLocal<T>>(this);

    /** Hash counter. */
    private static AtomicInteger hashCounter = new AtomicInteger(0);

    /**

    • Internal hash. We deliberately don't bother with #hashCode().
    • Hashes must be even. This ensures that the result of
    • (hash & (table.length - 1)) points to a key and not a value.
    • We increment by Doug Lea's Magic Number(TM) (*2 since keys are in
    • every other bucket) to help prevent clustering.
    • jxy 通過定義一個(gè)static的AtomicInteger類型變量hashCounter兆解,
    • 實(shí)現(xiàn)每一個(gè)ThreadLocal類實(shí)例的hash值不相同馆铁。即每次比上一次的值加1.
      */
      private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);

    /**

    • Per-thread map of ThreadLocal instances to values.
      */
      static class Values {

      /**

      • Size must always be a power of 2. 2 的冪次方
      • jxy 為什么必須是2的冪次方?
      • 在進(jìn)行hash計(jì)算的時(shí)候锅睛,是使用:int index = hash & values.mask;
      •                            mask = table.length - 1;
        
      •                            table = new Object[capacity * 2];
        
      • 只有當(dāng)INITIAL_SIZE為2的冪次方時(shí)埠巨,mask才可以是類似:00001111的值,在擴(kuò)充table時(shí)也是通過*2來計(jì)算capacity的
      • hash每次的值不一樣现拒,這樣計(jì)算出的index(即取hash最后幾位的值)就是唯一的辣垒。
        */
        private static final int INITIAL_SIZE = 16;

      /**

      • Placeholder for deleted entries.
      • jxy 為什么要定義tombstone?
      • 因?yàn)橐粋€(gè)線程中可以定義多個(gè)ThreadLocal印蔬,但是values中的table并不是在定義ThreadLocal時(shí)校驗(yàn)是否需要擴(kuò)充勋桶,
      • 當(dāng)定義量大于capacity時(shí),此時(shí)index = hash & values.mask有可能得到重復(fù)的值侥猬,所以要跟蹤哪些entry已經(jīng)廢棄
      • 在put時(shí)需要進(jìn)行tombstone的判斷處理例驹。
        */
        private static final Object TOMBSTONE = new Object();

      /**

      • Map entries. Contains alternating keys (ThreadLocal) and values.
      • The length is always a power of 2.
      • jxy table中每一個(gè)entry是有三個(gè)狀態(tài)的,null沒有進(jìn)行過設(shè)置退唠,tombstone鹃锈,設(shè)置過但是現(xiàn)在廢棄了,正常設(shè)置狀態(tài)
        */
        private Object[] table;

      /** Used to turn hashes into indices. */
      private int mask;

      /** Number of live entries. */
      private int size;

      /** Number of tombstones. */
      private int tombstones;

      /** Maximum number of live entries and tombstones. */
      private int maximumLoad;

      /** Points to the next cell to clean up. */
      private int clean;

      /**

      • Constructs a new, empty instance.
        */
        Values() {
        initializeTable(INITIAL_SIZE);
        this.size = 0;
        this.tombstones = 0;
        }

      /**

      • Used for InheritableThreadLocals.
        */
        Values(Values fromParent) {
        this.table = fromParent.table.clone();
        this.mask = fromParent.mask;
        this.size = fromParent.size;
        this.tombstones = fromParent.tombstones;
        this.maximumLoad = fromParent.maximumLoad;
        this.clean = fromParent.clean;
        inheritValues(fromParent);
        }

      /**

      • Inherits values from a parent thread.
        */
        @SuppressWarnings({"unchecked"})
        private void inheritValues(Values fromParent) {
        // Transfer values from parent to child thread.
        Object[] table = this.table;
        for (int i = table.length - 2; i >= 0; i -= 2) {
        Object k = table[i];

         if (k == null || k == TOMBSTONE) {
             // Skip this entry.
             continue;
         }
        
         // The table can only contain null, tombstones and references.
         Reference<InheritableThreadLocal<?>> reference = (Reference<InheritableThreadLocal<?>>) k;
         // Raw type enables us to pass in an Object below.
         InheritableThreadLocal key = reference.get();
         if (key != null) {
             // Replace value with filtered value.
             // We should just let exceptions bubble out and tank
             // the thread creation
             table[i + 1] = key.childValue(fromParent.table[i + 1]);
         } else {
             // The key was reclaimed.
             table[i] = TOMBSTONE;
             table[i + 1] = null;
             fromParent.table[i] = TOMBSTONE;
             fromParent.table[i + 1] = null;
        
             tombstones++;
             fromParent.tombstones++;
        
             size--;
             fromParent.size--;
         }
        

        }
        }

      /**

      • Creates a new, empty table with the given capacity.
        */
        private void initializeTable(int capacity) {
        this.table = new Object[capacity * 2];
        this.mask = table.length - 1;
        this.clean = 0;
        this.maximumLoad = capacity * 2 / 3; // 2/3
        }

      /**

      • Cleans up after garbage-collected thread locals.

      • jxy 每次在新增或刪除entry后需要重新檢查table狀態(tài)瞧预,進(jìn)行更新
        */
        private void cleanUp() {
        if (rehash()) {
        // If we rehashed, we needn't clean up (clean up happens as
        // a side effect).
        return;
        }

        if (size == 0) {
        // No live entries == nothing to clean.
        return;
        }

        // Clean log(table.length) entries picking up where we left off
        // last time.
        int index = clean;
        Object[] table = this.table;
        // jxy 此處沒有全部遍歷所有的item屎债,猜測是防止性能問題
        for (int counter = table.length; counter > 0; counter >>= 1, index = next(index)) {
        Object k = table[index];

         if (k == TOMBSTONE || k == null) {
             continue; // on to next entry
         }
        
         // The table can only contain null, tombstones and references.
         @SuppressWarnings("unchecked")
         Reference<ThreadLocal<?>> reference = (Reference<ThreadLocal<?>>) k;
         if (reference.get() == null) {
             // This thread local was reclaimed by the garbage collector.
             table[index] = TOMBSTONE;
             table[index + 1] = null;
             tombstones++;
             size--;
         }
        

        }

        // Point cursor to next index.
        clean = index;
        }

      /**

      • Rehashes the table, expanding or contracting it as necessary.

      • Gets rid of tombstones. Returns true if a rehash occurred.

      • We must rehash every time we fill a null slot; we depend on the

      • presence of null slots to end searches (otherwise, we'll infinitely

      • loop).
        */
        private boolean rehash() {
        if (tombstones + size < maximumLoad) {
        return false;
        }

        int capacity = table.length >> 1;

        // Default to the same capacity. This will create a table of the
        // same size and move over the live entries, analogous to a
        // garbage collection. This should only happen if you churn a
        // bunch of thread local garbage (removing and reinserting
        // the same thread locals over and over will overwrite tombstones
        // and not fill up the table).
        int newCapacity = capacity;

        if (size > (capacity >> 1)) {
        // More than 1/2 filled w/ live entries.
        // Double size.
        newCapacity = capacity * 2;
        }

        Object[] oldTable = this.table;

        // Allocate new table.
        initializeTable(newCapacity);

        // We won't have any tombstones after this.
        this.tombstones = 0;

        // If we have no live entries, we can quit here.
        if (size == 0) {
        return true;
        }

        // Move over entries.
        for (int i = oldTable.length - 2; i >= 0; i -= 2) {
        Object k = oldTable[i];
        if (k == null || k == TOMBSTONE) {
        // Skip this entry.
        continue;
        }

         // The table can only contain null, tombstones and references.
         @SuppressWarnings("unchecked")
         Reference<ThreadLocal<?>> reference = (Reference<ThreadLocal<?>>) k;
         ThreadLocal<?> key = reference.get();
         if (key != null) {
             // Entry is still live. Move it over.
             add(key, oldTable[i + 1]);
         } else {
             // The key was reclaimed.
             size--;
         }
        

        }

        return true;
        }

      /**

      • Adds an entry during rehashing. Compared to put(), this method
      • doesn't have to clean up, check for existing entries, account for
      • tombstones, etc.
        */
        void add(ThreadLocal<?> key, Object value) {
        for (int index = key.hash & mask;; index = next(index)) {
        Object k = table[index];
        if (k == null) {
        table[index] = key.reference;
        table[index + 1] = value;
        return;
        }
        }
        }

      /**

      • 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;

        // jxy 為什么需要循環(huán),和TOMBSTONE定義說明結(jié)合
        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;
         }
        

        }
        }

      /**

      • Gets value for given ThreadLocal after not finding it in the first

      • slot.
        */
        Object getAfterMiss(ThreadLocal<?> key) {
        Object[] table = this.table;
        int index = key.hash & mask;

        // If the first slot is empty, the search is over.
        if (table[index] == null) {
        Object value = key.initialValue();

         // If the table is still the same and the slot is still empty...
         // jxy 此處為什么要增加判斷垢油?如果是多線程的話盆驹,此類的實(shí)例都應(yīng)該是在一個(gè)線程中運(yùn)行啊
        

// 增加判斷是因?yàn)樯厦鎘ey.initalValue()的調(diào)用,在ThreadLocal中無法預(yù)知使用者如何實(shí)現(xiàn)initalValue滩愁,如果在其中做了諸如put之類的操作有可能導(dǎo)致rehash操作躯喇,此時(shí)table就會不相等,出現(xiàn)錯(cuò)誤
if (this.table == table && table[index] == null) {
table[index] = key.reference;
table[index + 1] = value;
size++;

                cleanUp();
                return value;
            }

            // The table changed during initialValue().
            put(key, value);
            return value;
        }

        // Keep track of first tombstone. That's where we want to go back
        // and add an entry if necessary.
        int firstTombstone = -1;

        // Continue search.
        for (index = next(index);; index = next(index)) {
            Object reference = table[index];
            if (reference == key.reference) {
                return table[index + 1];
            }

            // If no entry was found...
            if (reference == null) {
                Object value = key.initialValue();

                // If the table is still the same...
                if (this.table == table) {
                    // If we passed a tombstone and that slot still
                    // contains a tombstone...
                    if (firstTombstone > -1
                            && table[firstTombstone] == TOMBSTONE) {
                        table[firstTombstone] = key.reference;
                        table[firstTombstone + 1] = value;
                        tombstones--;
                        size++;

                        // No need to clean up here. We aren't filling
                        // in a null slot.
                        return value;
                    }

                    // If this slot is still empty...
                    if (table[index] == null) {
                        table[index] = key.reference;
                        table[index + 1] = value;
                        size++;

                        cleanUp();
                        return value;
                    }
                }

                // The table changed during initialValue().
                put(key, value);
                return value;
            }

            if (firstTombstone == -1 && reference == TOMBSTONE) {
                // Keep track of this tombstone so we can overwrite it.
                firstTombstone = index;
            }
        }
    }

    /**
     * Removes entry for the given ThreadLocal.
     */
    void remove(ThreadLocal<?> key) {
        cleanUp();

        for (int index = key.hash & mask;; index = next(index)) {
            Object reference = table[index];

            if (reference == key.reference) {
                // Success!
                table[index] = TOMBSTONE;
                table[index + 1] = null;
                tombstones++;
                size--;
                return;
            }

            if (reference == null) {
                // No entry found.
                return;
            }
        }
    }

    /**
     * Gets the next index. If we're at the end of the table, we wrap back
     * around to 0.
     */
    private int next(int index) {
        return (index + 2) & mask;
    }
}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末硝枉,一起剝皮案震驚了整個(gè)濱河市廉丽,隨后出現(xiàn)的幾起案子秸讹,更是在濱河造成了極大的恐慌,老刑警劉巖雅倒,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異弧可,居然都是意外死亡蔑匣,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進(jìn)店門棕诵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來裁良,“玉大人,你說我怎么就攤上這事校套〖燮ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵笛匙,是天一觀的道長侨把。 經(jīng)常有香客問我,道長妹孙,這世上最難降的妖魔是什么秋柄? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蠢正,結(jié)果婚禮上骇笔,老公的妹妹穿的比我還像新娘。我一直安慰自己嚣崭,他們只是感情好笨触,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著雹舀,像睡著了一般芦劣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上葱跋,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天持寄,我揣著相機(jī)與錄音,去河邊找鬼娱俺。 笑死稍味,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的荠卷。 我是一名探鬼主播模庐,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼油宜!你這毒婦竟也來了掂碱?” 一聲冷哼從身側(cè)響起怜姿,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤厉萝,失蹤者是張志新(化名)和其女友劉穎蚕钦,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嫩实,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡醉者,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年但狭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撬即。...
    茶點(diǎn)故事閱讀 39,769評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡立磁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出剥槐,到底是詐尸還是另有隱情唱歧,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布粒竖,位于F島的核電站颅崩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏温圆。R本人自食惡果不足惜挨摸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望岁歉。 院中可真熱鬧得运,春花似錦、人聲如沸锅移。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽非剃。三九已至置逻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間备绽,已是汗流浹背券坞。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肺素,地道東北人恨锚。 一個(gè)月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像倍靡,于是被迫代替她去往敵國和親猴伶。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 為啥要寫這篇文章 起初我看Handler相關(guān)源碼,看到Looper里面有個(gè)ThreadLocal他挎,如下筝尾,而這個(gè)Th...
    xufang2閱讀 2,667評論 4 51
  • 概述 ThreadLocal是一種線程內(nèi)部存儲類。通過他存儲的數(shù)據(jù)在不同的線程操作而互不干擾办桨,類似于各個(gè)線程中都有...
    皮球側(cè)飛閱讀 471評論 0 8
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法筹淫,類相關(guān)的語法,內(nèi)部類的語法呢撞,繼承相關(guān)的語法贸街,異常的語法,線程的語...
    子非魚_t_閱讀 31,625評論 18 399
  • 一捐川、基本數(shù)據(jù)類型 注釋 單行注釋:// 區(qū)域注釋:/* */ 文檔注釋:/** */ 數(shù)值 對于byte類型而言...
    龍貓小爺閱讀 4,261評論 0 16
  • 讀《詩的八堂課》的情景不難忘脓鹃,地鐵中、餐館中古沥、公園中瘸右、宿舍中……難忘的是我重讀這本書的八個(gè)夜晚,一天重讀一堂課岩齿,一...
    陳素封閱讀 641評論 1 8