ThreadLocal源码解读

来源:互联网 发布:免费无限php空间 编辑:程序博客网 时间:2024/05/20 05:52

  在学习Handler消息机制中Looper源码时看到ThreadLocal这个类,发现它很强大并且很方便的实现了对各个线程中Looper的管理。这个类的源码只有600行。下面先上一个简单的例子:

public class ThreadLocalTest {    static ThreadLocal<Integer> intLocals = new ThreadLocal<Integer>(){        protected Integer initialValue() {            return 1;        }    };    public static void main(String[] args) {        intLocals.set(669);        new MyThread("A线程").start();        System.out.println(Thread.currentThread().getName() + "===="                + intLocals.get());    }    static class MyThread extends Thread {        public MyThread(String name){            super(name);        }        @Override        public void run() {            System.out.println(Thread.currentThread().getName() + "===="                    + intLocals.get());        }    }}

这段代码的运行结果:

main====669A线程====1

提出问题:用static关键字修饰的静态变量intLocals没效果吗,A线程中的输出结果不应该是669吗?
下面带着这个疑问去源码中寻找答案:
ThreadLocal类中只有一个空参的构造方法,所以关键的代码只有initLocals.set(669)initLocals.get()了。
set方法源码:

    public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }    /**     * @param  t the current thread     */    ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }

从源码中可以看出在Thread中定义了一个ThreadLocalMap的引用,如果该引用的对象不为null就会通过ThreadLocalMap的引用来调用set方法。ThreadLocalMap是ThreadLocal的一个静态内部类,而在ThreadLocalMap中还定义的一个静态的Entry类。以ThreadLocal作为键、Object为值的数据结构;并将键存放到了WeakReference中,即线程中没有ThreadLocal的其他引用时就会自动回收。如果map为空就会执行createMap()方法创建一个ThreadLocalMap对象。

    /**     * The entries in this hash map extend WeakReference, using     * its main ref field as the key (which is always a     * ThreadLocal object).  Note that null keys (i.e. entry.get()     * == null) mean that the key is no longer referenced, so the     * entry can be expunged from table.  Such entries are referred to     * as "stale entries" in the code that follows.     */    static class Entry extends WeakReference<ThreadLocal<?>> {        /** The value associated with this ThreadLocal. */        Object value;        Entry(ThreadLocal<?> k, Object v) {            super(k);            value = v;        }    }    // 默认的数组大小为16,自定义的话必须为2的幂    private static final int INITIAL_CAPACITY = 16;    private Entry[] table;

由此可见map.set(this, value)只是将当前的intLocals对象作为键,669作为值存储到主线程中。
get方法源码:

    /**     * Returns the value in the current thread's copy of this     * thread-local variable.  If the variable has no value for the     * current thread, it is first initialized to the value returned     * by an invocation of the {@link #initialValue} method.     *     * @return the current thread's value of this thread-local     */    public T get() {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null) {            ThreadLocalMap.Entry e = map.getEntry(this);            if (e != null) {                @SuppressWarnings("unchecked")                T result = (T)e.value;                return result;            }        }        return setInitialValue();    }

get()通过获取当前线程的ThreadLocalMap对象map,遍历map中数组拿到键为this(即当前ThreadLocal的引用)的Entry实体,从而返回对应的键;map为空时就返回初始化时的值。
  看了上面的源码应该能够解答示例中的疑问了:ThreadLocal中set(value)方法将调用这个方法的对象作为键、value为值存储到当前线程中ThreadLocalMap中;而get()方法取出时是根据调用这个方法的ThreadLocal对象到当前线程的ThreadLocalMap中查找对应的值,为空时就返回初始值。也就是说ThreadLocal的get和set方法的操作对象其实都是执行这两个方法所在线程的ThreadLocalMap对象,ThreadLocal只起到了一个键的作用。
  ThreadLocal将对象的访问范围限制在线程中,并且当线程结束后ThreadLocal会被自动回收,也可以调用remove()(since 1.5)方法去掉线程中保存的变量。
  另外,ThreadLocal不是为了解决线程间的同步问题,感觉恰恰相反,它是为了避免产生同步问题。既然如此,为什么又要整出ThreadLocal这么个东西,直接搞个局部变量不就好了吗?这个问题暂时不知道咋回答,以后再补上!!!

参考资料
Android7.0源码
彻底理解ThreadLocal

  写个博客像挤牙膏样,逻辑混乱,感觉很多东西看了源码就那么回事,但是要用文字表达出来总是说不清楚,不知道怎么回事,希望过来人指点一二!
  很烦躁…..

原创粉丝点击