`
貌似掉线
  • 浏览: 256636 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Android中的ThreadLocal源码解析

阅读更多
我在之前的文章《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比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类的设计的修改。这里不再赘述。
  • 大小: 9.5 KB
  • 大小: 13.4 KB
0
1
分享到:
评论
2 楼 貌似掉线 2013-09-12  
xiaozhi6156 写道
不明觉历,回家再看..LZ加油

==!
1 楼 xiaozhi6156 2013-09-12  
不明觉历,回家再看..LZ加油

相关推荐

Global site tag (gtag.js) - Google Analytics