android Handler机制之ThreadLocal详解

MilagrosHol 7年前
   <h2>概述</h2>    <p>我们在谈Handler机制的时候,其实也就是谈Handler、Message、Looper、MessageQueue之间的关系,对于其工作原理我们不做详解(Handler机制详解)。</p>    <ul>     <li>Message:Handler发送、接收和处理的消息对象</li>     <li>Looper:每个线程只能拥有一个Looper.它的looper()方法负责循环读取MessageQueue中的消息并将读取到的消息交给发送该消息的handler进行处理。</li>     <li>MessageQueue:消息队列,它采用先进先出的方式来管理Message。程序在创建Looper对象时,会在它的构造器中创建MessageQueue。</li>    </ul>    <h2>Handler类简析</h2>    <p>Handler类的主要作用有两个:在新启动的线程中发送消息;在主线程中获取和处理消息。</p>    <p>而要完整的理解Handler机制,对于Looper的底层存储和轮询机制是必须了解的,看过了其实就很简单,今天就专门讲这个。</p>    <h2>ThreadLocal详解</h2>    <p>为了方便大家理解,我们直接看源码:</p>    <pre>  <code class="language-java">public class ThreadLocal<T> {   .....  }</code></pre>    <p>这里可以看出threadlocal是一个范型类,这标志着threadlocal可以存储所有数据,作为存储数据来说,我们首先想到的是会对外提供set(),get(),remove(),等方法。</p>    <p>set()方法:</p>    <pre>  <code class="language-java">/**       * 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);      }</code></pre>    <p>从源码可以看出,首先获取当前线程,然后调用values方法,我们来看下values方法:</p>    <pre>  <code class="language-java">/**       * Gets Values instance for this thread and variable type.       */      Values values(Thread current) {          return current.localValues;      }</code></pre>    <p>该方法是返回当前线程的一个存储实类,那ThreadLocal又是什么呢?上面说过 ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据。</p>    <p>我们来看几个ThreadLocal方法,先回到set方法,得到values的实类以后会来一个判断,为null调用initializeValues(currentThread)</p>    <pre>  <code class="language-java">Values initializeValues(Thread current) {          return current.localValues = new Values();      }</code></pre>    <p>接下来调用value的put方法,我们想到的应该是往里面插值,也就是我们说的put()。</p>    <pre>  <code class="language-java">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;                  }              }          }</code></pre>    <p>从源码可以看出,把values的值传入到一个table数组的key.reference的下一个下标中,至此,我们了解了Threadlocal的存值过程,首先会获取当前线程,根据当前线程获取Values存储类,该存储类在该线程是单例的,在调用values存储类中的put方法,最终将存储的内容存储到Values内部类的table数组下标为key.reference中 。</p>    <p>接下来我们来看一下取值的方法:</p>    <pre>  <code class="language-java">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);      }</code></pre>    <p>ThreadLocal的get方法的逻辑也比较清晰,它同样是取出当前线程的localValues对象,如果这个对象为null那么就返回初始值,初始值由ThreadLocal的initialValue方法来描述。</p>    <pre>  <code class="language-java">protected T initialValue() {          return null;      }</code></pre>    <p>如果localValues对象不为null,那就取出它的table数组并找出ThreadLocal的reference对象在table数组中的位置,然后table数组中的下一个位置所存储的数据就是ThreadLocal的值。</p>    <p>接着我们再来看一下remove的方法实现:</p>    <pre>  <code class="language-java">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;                  }              }          }</code></pre>    <p>到此我们就完全明白了ThreadLocal的存取原理了:通过ThreadLocal的set和get方法可以看出,它们所操作的对象都是当前线程的localValues对象的table数组,因此在不同线程中访问同一个ThreadLocal的set和get方法,它们对ThreadLocal所做的读写操作仅限于各自线程的内部,这就是为什么ThreadLocal可以在多个线程中互不干扰地存储和修改数据。</p>    <p> </p>    <p>来自:http://blog.csdn.net/xiangzhihong8/article/details/53884474</p>    <p> </p>