android-----ThreadLocal源码分析

来源:互联网 发布:linux init.d不见了 编辑:程序博客网 时间:2024/05/20 08:22

        今天在复习Handler消息处理机制原理的时候,发现自己对android的ThreadLocal部分理解还不是很到位,在此做个总结,先来说说为什么会在Handler消息处理机制中出现ThreadLocal这个东西吧,我们都知道Handler发送消息到MessageQueue中,Looper从MessageQueue中取出消息来执行,但是Looper呢只是一个封装类而已,虽然它里面有loop方法,但是真正的运作还是需要线程的,那么Looper是怎么和线程联系到一起的呢?就是采用ThreadLocal了,之前我分析过java中的ThreadLocal源码,参见:java-----ThreadLocal源码分析,这篇我们分析的是android中的ThreadLocal源码,大家都知道解决多线程并发问题我们可以采用synchronized关键字来实现,他依托的是JVM的锁机制来实现临界区的变量、函数在CPU运行中访问的原子性;而ThreadLocal则是在每个线程中各自都保留一个共享内容的副本,自己修改自己的当然不会出现问题了,也就是说两者还是有本质区别的,使用synchronized之后所有线程修改的是同一个内容,一个线程对其的修改是会影响到另一个线程的访问该共享内容初始值的,只不过是按顺序修改而已,而采用ThreadLocal的话一个线程修改的内容对另一个线程来说是并不可见的,android中的ThreadLocal在java原生ThreadLocal的基础上进行了一些 优化操作,个人认为最大的优化地方在于采用数组的方式存放ThreadLocal引用以及值而不是之前的map方式;

        和原生态的ThreadLocal一样,android中的ThreadLocal源码最重要的方法也是set和get方法,首先看看set方法:

 public void set(T value) {        Thread currentThread = Thread.currentThread();        Values values = values(currentThread);        if (values == null) {            values = initializeValues(currentThread);        }        values.put(this, value);    }
        首先获取到当前正在执行的线程,并且通过values方法获取当前线程的Values值,其中Values是定义在ThreadLocal中的静态类,他的功能就是存储放进来的数据以及对数据进行处理,比如清理失效数据,扩展table数组等等,他是通过Object数组来进行存放数据的,及时清理无效的数据并且扩展或者缩小table的大小,便于保持当前table的健壮性,查看values方法:

 Values values(Thread current) {        return current.localValues;    }
        可以发现他仅仅是返回当前线程的localValues属性值而已,进入到Thread类中我们看到有如下代码:

  ThreadLocal.Values localValues;
        即localValues是Values类型的变量,回到set方法中,在通过values方法获得Values对象之后,判断该对象是否为空,如果为空的话,则执行initializeValues方法:

Values initializeValues(Thread current) {        return current.localValues = new Values();    }
        可以看到该方法仅仅只是创建一个Values对象出来,并且将其赋给当前线程的localValues属性;

        回到set方法中,最后我们执行了values的put方法:

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

        在分析put方法之前我们需要了解一点,在ThreadLocal类中定义了一个Reference变量:

  private final Reference<ThreadLocal<T>> reference            = new WeakReference<ThreadLocal<T>>(this);
        他是一个弱引用,引用了ThreadLocal实例自己;
        简单分析下put方法,从这段源码中我们看到了大部分的操作都是在修改table数组内容,他是一个Object类型的数组,定义在Values方法中,是他的一个属性,长度总是2乘方倍数,ThreadLocal以及其对应的值都是连续的存储在这个数组中的,具体来说ThreadLocal的reference值存储在其对应值的前一个位置处,通过key.hash & mask获取到当前ThreadLocal在table数组中的index值,关于key.hash & mask的奇妙之处就在于它能够保证获取到的index值不会越界同时呢也能保证对于不同的ThreadLocal得到的index值是不同的这一点,具体是通过斐波拉契散列寻址方式实现的,其中mask即计算下标的掩码,其值为table的长度减1,从上面的put方法中我们最能够直观的看出ThreadLocal的值在table数组中的存储位置为此ThreadLocal的reference字段所标识对象的下一个位置,比如:

 table[index] = key.reference; table[index + 1] = value;

那么index存储的就是当前ThreadLocal的reference弱引用,index+1存储的就是当前ThreadLocal的值;

        其实这点在Values的add方法中是最能直观的看出来的:

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

        接下来看看他的get方法:

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);    }
        同样首先获取当前正在执行的线程,接着获取当前线程的localValues属性值,这个和set方法是一样的,如果当前线程的localValues属性值不为空的话,则取出他的table数组,并且找出当前ThreadLocal对象的reference对应的index值,返回table数组中index+1位置上的值,也就是对应于当前ThreadLocal的值;如果当前线程的localValues属性值为空的话,则执行initializeValues方法,创建一个Values对象出来,并且通过values的getAfterMiss方法返回一个初始值对象,简单查看getAfterMiss源码之后发现这个初始值是通过下面代码获取的:

 Object value = key.initialValue();
        也就是调用的ThreadLocal的initialValue方法,这个方法是protected修饰的方法,我们也可以通过继承ThreadLocal类来重写这个方法,默认情况下是返回null的;

 protected T initialValue() {        return null;    }
        至此,android中的ThreadLocal主要源码分析完毕,下面做个小结:

        (1)android中的ThreadLocal值是存储在Values静态类中的table数组中的,这个数组中的0,2,4....2n下标存储的是ThreadLocal对应的reference值,而1,3,5....(2n+1)下标存储的是对应于reference的ThreadLocal值,某种意义上这里的table数组是类似于map方式的;

        (2)寻址的时候是采用斐波那契散列的方式获得index值的


0 0