android Handler機制之ThreadLocal詳解

MilagrosHol 7年前發布 | 5K 次閱讀 安卓開發 Android開發 移動開發

概述

我們在談Handler機制的時候,其實也就是談Handler、Message、Looper、MessageQueue之間的關系,對于其工作原理我們不做詳解(Handler機制詳解)。

  • Message:Handler發送、接收和處理的消息對象
  • Looper:每個線程只能擁有一個Looper.它的looper()方法負責循環讀取MessageQueue中的消息并將讀取到的消息交給發送該消息的handler進行處理。
  • MessageQueue:消息隊列,它采用先進先出的方式來管理Message。程序在創建Looper對象時,會在它的構造器中創建MessageQueue。

Handler類簡析

Handler類的主要作用有兩個:在新啟動的線程中發送消息;在主線程中獲取和處理消息。

而要完整的理解Handler機制,對于Looper的底層存儲和輪詢機制是必須了解的,看過了其實就很簡單,今天就專門講這個。

ThreadLocal詳解

為了方便大家理解,我們直接看源碼:

public class ThreadLocal<T> {
 .....
}

這里可以看出threadlocal是一個范型類,這標志著threadlocal可以存儲所有數據,作為存儲數據來說,我們首先想到的是會對外提供set(),get(),remove(),等方法。

set()方法:

/**

 * 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);
}</code></pre> 

從源碼可以看出,首先獲取當前線程,然后調用values方法,我們來看下values方法:

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

該方法是返回當前線程的一個存儲實類,那ThreadLocal又是什么呢?上面說過 ThreadLocal是一個線程內部的數據存儲類,通過它可以在指定的線程中存儲數據,數據存儲以后,只有在指定線程中可以獲取到存儲的數據。

我們來看幾個ThreadLocal方法,先回到set方法,得到values的實類以后會來一個判斷,為null調用initializeValues(currentThread)

Values initializeValues(Thread current) {
        return current.localValues = new Values();
    }

接下來調用value的put方法,我們想到的應該是往里面插值,也就是我們說的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;
                }
            }
        }

從源碼可以看出,把values的值傳入到一個table數組的key.reference的下一個下標中,至此,我們了解了Threadlocal的存值過程,首先會獲取當前線程,根據當前線程獲取Values存儲類,該存儲類在該線程是單例的,在調用values存儲類中的put方法,最終將存儲的內容存儲到Values內部類的table數組下標為key.reference中 。

接下來我們來看一下取值的方法:

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);
    }

ThreadLocal的get方法的邏輯也比較清晰,它同樣是取出當前線程的localValues對象,如果這個對象為null那么就返回初始值,初始值由ThreadLocal的initialValue方法來描述。

protected T initialValue() {
        return null;
    }

如果localValues對象不為null,那就取出它的table數組并找出ThreadLocal的reference對象在table數組中的位置,然后table數組中的下一個位置所存儲的數據就是ThreadLocal的值。

接著我們再來看一下remove的方法實現:

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

到此我們就完全明白了ThreadLocal的存取原理了:通過ThreadLocal的set和get方法可以看出,它們所操作的對象都是當前線程的localValues對象的table數組,因此在不同線程中訪問同一個ThreadLocal的set和get方法,它們對ThreadLocal所做的讀寫操作僅限于各自線程的內部,這就是為什么ThreadLocal可以在多個線程中互不干擾地存儲和修改數據。

 

來自:http://blog.csdn.net/xiangzhihong8/article/details/53884474

 

 本文由用戶 MilagrosHol 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!