android之ThreadLocal详解

threadlocal并不是线程,记得最早几年前面试android的时候,被人问到threadlocal当时没听过,直接回答说是线程,现在想想当时真怂。threadlocal而是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据,说的比较抽象,直接看源码来解释

**
 * 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
 */
public class ThreadLocal<T> {

可以看出threadlocal是一个范型类,这标志着threadlocal可以存储所有数据,作为存储数据来说,我们首先想到的是会对外提供set(),get(),remove(),等方法,顺着我们的想法来看源码,果然如此,
1,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);
    }

看官方的对该方法的注释可以知道,该方法负责往当前线程插值,这个值可以为null,从源码可以看出,首先获取当前线程,然后调用values方法,我们来看下values方法

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

可以看出该方法是返回当前线程的一个存储实类,那么这个类是什么呢,点进Thread类中看到

  /**
     * Normal thread local values.
     */
    ThreadLocal.Values localValues;

是ThreadLocal的一个内部类Values,我们暂时不看这个内部类的实现,先回到set方法,得到values的实类以后会来一个判断,为null调用initializeValues(currentThread),从名字看出,好像是初始化Values的一个方法,我们进到方法来看

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

果然是的,对当前线程的localValues初始化,原来真正初始化Values是这个方法,下面调用value的put方法,我们想到的应该是往里面插值,顺着我们的疑问,我们来看下Values这个内部类的put方法

 /**
         * 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;

            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中
下面我们来了解下get()方法

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

官方注释可以看出该方法返回一个当前线程的当前value值,如果这个值没有初始化,那么会通过initialValue();返回一个null

 /**
     * Provides the initial value of this variable for the current thread.
     * The default implementation returns {@code null}.
     *
     * @return the initial value of the variable.
     */
    protected T initialValue() {
        return null;
    }

下面我们来看源码实现,和set方法一样,首先获取当前线程,再通过values(currentThread); 获取当前线程的Values对象,判断values是否为null, 如果当前线程没有赋值过 那么会调用上述initialValue()方法返回一个null,否则会通过value.table获取当前线程的value中的table数组,再根据set的下标reference的下一个得到当前值
接着我们再来看一下remove的方法实现

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

该方法就没什么好解释的了,就是手动的去释放当前线程的存储的值删除,为了更好的内存释放

发布了72 篇原创文章 · 获赞 44 · 访问量 7万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览