1. 程式人生 > >ThreadLocal原始碼解析

ThreadLocal原始碼解析

 ThreadLocal不是一個具體的執行緒。它是一個執行緒內部的資料儲存類,通過它可以再指定的執行緒中儲存資料,資料儲存以後,只有在指定執行緒中可以獲取到儲存的資料,對於其它執行緒來說則無法獲取到資料。  ThreadLocal之所以有這麼神奇的效果,是因為不同執行緒訪問同一個ThreadLocal的get方法,ThreadLocal內部會將各自執行緒的引用當做table陣列的一個值存在,然後從陣列中根據當前ThreadLocal的reference去查找出相應的value。這就是為什麼通過ThreadLocal可以再不同執行緒中維護一套資料的副本並且彼此互不干擾。  在java中ThreadLocal以Map的形式儲存資料(ThreadLocal物件為 key  數值為value)。在Android中做了些改動,在Thread-Local的add方法中,可以看到它會把ThradLocal物件(key)和相對應的value放在table陣列連續的位置中。 也就是table被設計為下標為0,2,4...2n的位置存放key,而1,3,5...(2n +1 )的位置存放value。
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;
                }
            }
        }

類中最重要的兩個方法是get(),set()。下面開始分析set()原始碼。
    public void set(T value ) {
        Thread currentThread = Thread.currentThread();
        Values values = values( currentThread);
        if (values == null) {
            values = initializeValues(currentThread );
        }
        values.put( this, value );
    }
首先獲取當前執行緒物件currentThread,然後執行values(currentThread)方法。原始碼如下:
Values values(Thread current) {
      return current .localValues;
  }
   在values (currentThread )中返回了currentThread .localValues。跟進Thread的原始碼可以發現:這個currentThread .localValues其實就是ThreadLocal.Values  localValues 。Values 是ThreadLocal中的一個靜態內部類。此時獲取到返回的Values物件。     接下來進行判空操作,如果返回的values為空,那麼再次例項化currentThread。跟進initializeValue-s(currentThread)可以發現
Values initializeValues(Thread current) {
        return current .localValues = new Values();
    }

new Values()的例項化過程:

       Values() {
            initializeTable( INITIAL_SIZE);//INITIAL_SIZE 預設值16
            this.size = 0;
            this.tombstones = 0;
        }

      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
        }
此時可以確保有了Values的一個例項,接下來就可以執行values .put(this, value ),跟進put方法
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 ;
                }
            }
        }

依據上面的程式碼可以得出一個儲存規則:ThreadLocal的值在table陣列中的儲存位置總是為reference欄位所表示的物件的下一個位置。table[index] =key.reference;table[index+ 1] =value;最終ThreadLocal的值會被儲存在table陣列中:table[index+ 1] =value;至此,set方法解析完畢。下面看一下get方法。
public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        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);
    }
看完set方法後再看get就比較簡單了,首先得到一個Values物件,然後求出table陣列ThreadLocal.reference的下標。前文說過:ThradLocal物件(key)和相對應的value放在table陣列連續的位置中。 也就是table被設計為下標為0,2,4...2n的位置存放key,而1,3,5...(2n +1 )的位置存放value。現在得到index後再index+1就是value在table陣列中的下標。即value=table[index+1];return value即可。到此想必讀者對ThreadLocal為什麼能在不同執行緒中能夠為不同執行緒建立不同的執行緒副本(其實不太準確,應該是相同物件的不同值),原因就在於採用了key value形式的table陣列。key為不同執行緒的reference,value就五花八門了。        ThreadLocal淺析到此結束。謝謝欣賞~ 更多Framework原始碼解析,請移步