Java ThreadLocal源碼分析


ThreadLocal,可以理解爲線程本地變量。通過ThreadLocal保存的值在同一線程中是數據共享的,而在不同線程中是互斥的(所謂互斥這裏可以理解爲同一個ThreadLocal變量在不同線程中值是不同的,在一個線程中改變ThreadLocal的值也不會影響另外一個線程中的值)。

ThreadLocal實例演示

首先,聲明一個ThreadLocal的變量,然後在UI線程,子線程1中設置對應的值,在子線程2中不設置值。要想給ThreadLocal變臉設置值,只要調用set方法即可,並通過get方法可以獲取對應的值。下面通過一個簡單demo演示下ThreadLocal的用法。

代碼如下:

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread() {
            @Override
            public void run() {
                // 1、在子線程1中設置值
                mThreadLocalStr.set("thread1");
                System.out.println(Thread.currentThread() + " " + mThreadLocalStr.get());
            };
        }.start();

        new Thread() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread() + " " + mThreadLocalStr.get());
            };
        }.start();

        // 3、在主線程中設置值
        mThreadLocalStr.set("main");
        System.out.println(Thread.currentThread() + " " + mThreadLocalStr.get());

    }

    /**
     * 申明一個字符串型ThreadLocal變量
     */
    public ThreadLocal<String> mThreadLocalStr = new ThreadLocal<String>();

}

日誌:

02-15 20:24:00.885: I/System.out(2225): Thread[main,5,main] main
02-15 20:24:00.885: I/System.out(2225): Thread[Thread-143,5,main] thread1
02-15 20:24:00.886: I/System.out(2225): Thread[Thread-144,5,main] null

通過日誌打印結果我們可知:在UI線程中和子線程中分別打印了線程中的值,而在Thread2中因爲沒有設置ThreadLocal,所以爲null,這也印證了上面所提到的ThreadLocal在不同線程中值是獨立的。

ThreadLocal源碼分析

ThreadLocal方法列表:

  • set():將此線程局部變量的當前線程副本中的值設置爲指定值
  • get(): 返回此線程局部變量的當前線程副本中的值,如果沒有設置值就會返回initialValue()方法中指定的值
  • initialValue():返回此線程局部變量的當前線程的“初始值”
  • remove(): 移除此線程局部變量當前線程的值

通過ThreadLocal源碼可知,ThreadLocal是一個泛型類,通過泛型方法set可以設置ThreadLocal的值,通過get可以獲取ThreadLocal的值。

在ThreadLocal中還定義了一個內部靜態類Values,該類是存儲ThreadLocal變量的具體實現,在該類中定義了一個數組,就是用於存儲線程以及ThreadLocal變量。


public class ThreadLocal<T> {


    public ThreadLocal() {}

    /**
     *
     * 獲取當前線程的ThreadLocal的值
     */
    @SuppressWarnings("unchecked")
    public T get() {

        // 獲取當前線程的values對象
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);

        if (values != null) {
            Object[] table = values.table;

            // 計算存儲key所在的index,然後獲取key對應的值,在存儲的時候也是通過hash的方式計算index
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            // 如果沒有存儲,那麼會初始化current.localValues = new Values();
            values = initializeValues(currentThread);
        }

        // 如果沒有設置值,會返回initialValue()中的值作爲默認值
        return (T) values.getAfterMiss(this);
    }

    /**
     *  設置默認值
     */
    protected T initialValue() {
        return null;
    }

    /**
     *  設置當前線程的ThreadLocal的值
     */
    public void set(T value) {

        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);

        if (values == null) {
            values = initializeValues(currentThread);
        }

        values.put(this, value);
    }

    /**
     *  移除線程本地變臉
     */
    public void remove() {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            values.remove(this);
        }
    }

    /**
     * 初始化
     */
    Values initializeValues(Thread current) {
        return current.localValues = new Values();
    }

     /**
     * 在Thread類中定義了一個Values對象localValues,用於存儲本線程所有的ThreadLocal變量
     */
    Values values(Thread current) {
        return current.localValues;
    }

    private final Reference<ThreadLocal<T>> reference
            = new WeakReference<ThreadLocal<T>>(this);

    private static AtomicInteger hashCounter = new AtomicInteger(0);


    private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);

    /**
     * 
     * 內部類,實現存儲ThreadLocal的key-value的數據結構
     * 該類通過數組實現,key和value分別被存放在index、index+1的位置
     * 
     * 因爲一個Thread可能有多個不同的ThreadLocal變量,所在在計算key的index時,通過hash運算保證唯一
     */
    static class Values {

        /**
         * 將key-value存放在
         */
        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表示線程中存儲的本地變量的數量
                    size++;
                    return;
                }

                // Remember first tombstone.
                if (firstTombstone == -1 && k == TOMBSTONE) {
                    firstTombstone = index;
                }


            }
        }

        /**
         *  如果沒有設置線程的ThreadLocal值,那麼返回默認值並且存儲key-value值
         */
        Object getAfterMiss(ThreadLocal<?> key) {
            Object[] table = this.table;
            int index = key.hash & mask;

            if (table[index] == null) {

                // 獲取默認值
                Object value = key.initialValue();


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



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

        /**
         * 移除key-value
         */
        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;
                }
            }
        }

        /**
         * 清楚已經被GC清除的ThreadLocal變量
         */
        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;
            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;
        }


    }
}

ThreadLocal內存泄漏問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章