我在之前的文章《Android中的Looper,Handler及HandlerThread简析》(http://maosidiaoxian.iteye.com/blog/1927735)中有提到过ThreadLocal,后来一直想详细读读这个类。前几天写完了Java原生的ThreadLocal,今天来看一下Android中的ThreadLocal类。在读这篇文章之前,建议先读一下我前面写的关于Java中的ThreadLocal解析的两篇文章,因为在这里对一些相同的内容我会不再赘述。

Android中的ThreadLocal的源码在libcore/luni/src/main/java/java/lang目录下,如果你没有下载Android源码,可通过以下地址读到该类的源码:https://android.googlesource.com/platform/libcore/+/android-4.3_r2.2/luni/src/main/java/java/lang/ThreadLocal.java
在Android中,ThreadLocal像是对原来的ThreadLcal做了优化的实现。我们同样先看看这个类的结构,如下图:

Android中的ThreadLocal源码解析

Android中的ThreadLocal源码解析

可以直观地看到在android中ThreadLocal比java原生的这个类少了一些API,而且保存线程变量的内部类名字也改为Values,里面没有再定义内部类。仔细地阅读比较,我们可以看到Android中对Java原生的ThreadLocal做了一些优化的工作。
先来看看ThreadLocal的变量,代码如下:
    /** Weak reference to this thread local instance. */    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.     */    private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);
在这里,ThreadLocal没有再定义HASH_INCREMENT 这个常量,而是直接写在了hash变量的定义当中。将原来的nextHashCode的代码与hash的定义合在了一起,增量则是0x61c88647 * 2。
在这里还多定义了一个变量,Reference<ThreadLocal<T>> reference,它是一个弱引用,引用ThreadLocal实例的自己。
而当实例化一个ThreadLocal对象时,仅仅是生成一个hash值,和对reference赋值。
在set方法中,依然是从线程中取得保存变量的对象,在这里是values,如果values为null就进行初始化(对thread对象创建Values对象并返回),然后调用其put方法保存变量,与Java原生的思路都是一样的,只是代码简化了许多,如下:
    public void set(T value) {        Thread currentThread = Thread.currentThread();        Values values = values(currentThread);        if (values == null) {            values = initializeValues(currentThread);        }        values.put(this, value);    }    /**     * Gets Values instance for this thread and variable type.     */    Values values(Thread current) {        return current.localValues;    }    /**     * Creates Values instance for this thread and variable type.     */    Values initializeValues(Thread current) {        return current.localValues = new Values();    }


remove()方法就不谈了,很简单,跟原来的基本一致。
下面来看一下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);    }

我们会注意到其中的代码:
            if (this.reference == table[index]) {                return (T) table[index + 1];            }

为什么用hash取得下标后,下一位才是保存的变量值呢?
我们来看一下Values这个类是怎么定义和设计的。
Values是被设计用来保存线程的变量的一个类,它相当于一个容器,存储保存进来的变量。它的成员变量如下:
        /**         * Map entries. Contains alternating keys (ThreadLocal) and values.         * The length is always a power of 2.         */        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;

同样table是实际上保存变量的地方,但它在这里是个Object类型的数组,它的长度必须是2的n次方的值。mask即计算下标的掩码,它的值是table的长度-1。size表示存放进来的实体的数量。这与前面原生的ThreadLocal的ThreadLocalMap是一样的。但是在这里它还定义了三个int类型的变量:tombstones表示被删除的实体的数量,maximumLoad是一个阈值,用来判断是否需要进行rehash,clean表示下一个要进行清理的位置点。
我们来看一下当Values对象被创建时进行了什么工作,代码如下:
        /**         * Constructs a new, empty instance.         */        Values() {            initializeTable(INITIAL_SIZE);            this.size = 0;            this.tombstones = 0;        }        /**         * 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        }

上面的代码我们可以看到,当初始化一个Values对象时,它会创建一个长度为capacity*2的数组。
然后在add()方法当中,也可以看到它会把ThreadLocal对象(key)和对应的value放在连续的位置中。
        /**         * 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;                }            }        }

也就是table被设计为下标为0,2,4...2n的位置存放key,而1,3,5...(2n +1 )的位置存放value。直接通过下标存取线程变量,它比用WeakReference<ThreadLocal>类在内存占用上更经济,性能也更好。这也是前面中hash的增量要取0x61c88647*2的原因,它也保证了其二进制中最低位为0,也就是在计算key的下标时,一定是偶数位。
而在remove()方法中,移除变量时它是把对应的key的位置赋值为TOMBSTONE,value赋值为null,然后 tombstones++;size--;。TOMBSTONE是前面定义的一个常量,表示被删除的实体。
其他方法的算法,其实与Java原生的一样,只是做了对应于Values类的设计的修改。这里不再赘述。

更多相关文章

  1. android 创建系统变量
  2. Android应用程序添加自定义的property属性
  3. Android踩坑日记:自定义水平和圆形ProgressBar样式
  4. android 使用Intent传递数据之静态变量
  5. Android ListView元素间隙线自定义渐变效果

随机推荐

  1. android 判断文件是否存在
  2. Android之MediaPlayer(两种)基本使用方式
  3. C#内置泛型委托之Func委托
  4. C++单例模式的懒汉模式和饿汉模式详解
  5. ASP.NET CORE实现跨域
  6. C++的数据共享与保护你了解吗
  7. PHP json_encode中文乱码解决方法
  8. Vue.js中v-for指令的用法介绍
  9. C++成员函数中const的使用详解
  10. JavaScript属性操作