android threadlocal笔记

总所周知,threadlocal用的还是蛮多的,今天回顾android源码又看到它的影子,所以自己抽空做个笔记,先从一个简单的例子开始,该例子就是让每个线程拥有自己唯一的一个Student对象,具体代码如下

package com.threadlocalttest;
/**
 * 学生类
 * @author Administrator
 *
 */
public class Student {

 private ThreadLocal threadlocal = new ThreadLocal();

 public Student generateStuPerThread() {
  Student stu = (Student) threadlocal.get();
  if (stu == null) {
   stu = new Student();
   threadlocal.set(stu);

  }
  return stu;

 }

}

package com.threadlocalttest;
/**
 * 测试类
 * @author Administrator
 *
 */
public class TestMain {

 public  void begin()
 {
  
  new Thread(new WorkThread(new Student())).start();
  
 }
 public static void main(String[] args) {
  
  new TestMain().begin();
  new TestMain().begin();
  
  
  
 }
 
 
 private class  WorkThread  implements  Runnable{
       private Student  stu;
  public  WorkThread(Student   stu)
       {
        this.stu=stu;
       
       }
  
  @Override
  public void run() {

   try{
    System.out.println(stu.generateStuPerThread()+"=============>1");
    Thread.sleep(2000);
    System.out.println(stu.generateStuPerThread()+"=============>2");
    Thread.sleep(2000);
    System.out.println(stu.generateStuPerThread()+"=============>3");
    
    
   }catch(Exception  e)
   {
    e.printStackTrace();
   }
    
   
  }
  
 }
}

运行后的打印结果如下

com.threadlocalttest.Student@14318bb=============>1
com.threadlocalttest.Student@1a758cb=============>1
com.threadlocalttest.Student@14318bb=============>2
com.threadlocalttest.Student@1a758cb=============>2
com.threadlocalttest.Student@14318bb=============>3
com.threadlocalttest.Student@1a758cb=============>3

通过以上实验结果我们来分析下ThreadLocal的内部机制

下面来看看该类的实现原理

首先看看该类的官方解释

 * <p>Each thread holds an implicit reference to its copy of a thread-local
 * variable as long as the thread is alive and the <tt>ThreadLocal</tt>
 * instance is accessible; after a thread goes away, all of its copies of
 * thread-local instances are subject to garbage collection (unless other
 * references to these copies exist).

大概意思不用说也明白了,只要线程存活它便拥有一个ThreadLocal引用,而且它是可访问的,一旦线程运行结束,所有的threadlocal副本就会被gc回收(除非对这些副本的其它引用还存在)

 

ThreadLocal类主要有三个成员变量

    /**
     * ThreadLocals rely on per-thread hash maps attached to each thread
     * (Thread.threadLocals and inheritableThreadLocals).  The ThreadLocal
     * objects act as keys, searched via threadLocalHashCode.  This is a
     * custom hash code (useful only within ThreadLocalMaps) that eliminates
     * collisions in the common case where consecutively constructed
     * ThreadLocals are used by the same threads, while remaining well-behaved
     * in less common cases.
     */

  //threadlocal就是通过每个线程的hash maps与每个线程进行关联的,threadlocal作为key,是通过threadLocalHashCode找到的,通过使用该种方式,当多个线程连续访问threadlocals是可以避免冲突
    private final int threadLocalHashCode = nextHashCode();

    /**
     * The next hash code to be given out. Accessed only by like-named method.
     */
    private static int nextHashCode = 0;

    /**
     * The difference between successively generated hash codes - turns
     * implicit sequential thread-local IDs into near-optimally spread
     * multiplicative hash values for power-of-two-sized tables.
     */
    private static final int HASH_INCREMENT = 0x61c88647;

 

在看看该类的两大重要方法

首先是set方法

    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Many applications will have no need for
     * this functionality, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current threads' copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

通过该方法将threadlocal对象与用户想要保存在线程里面的对象绑定,(ThreadLocalMap 是ThreadLocal的嵌套类,通过该类的set方法,将threadlocal对象与保存的对象进行绑定

 

        /**
         * Set the value associated with key.
         *
         * @param key the thread local object
         * @param value the value to be set
         */
        private void set(ThreadLocal key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
   e != null;
   e = tab[i = nextIndex(i, len)]) {
                ThreadLocal k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i, false);
                    return;
                }
            }

//将threadlocal的threadLocalHashCode作为entry数组的下标,并生成一个entry条目,并用下标指向

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

 

ThreadLocalMap 类内部内部嵌套类entry的声明如下

 

private static class Entry extends WeakReference<ThreadLocal> {
            /** The value associated with this ThreadLocal. */
            private Object value;

            private Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }

然后是get方法

    /**
     * Returns the value in the current thread's copy of this thread-local
     * variable.  Creates and initializes the copy if this is the first time
     * the thread has called this 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)
            return (T)map.get(this);

        // Maps are constructed lazily.  if the map for this thread
        // doesn't exist, create it, with this ThreadLocal and its
        // initial value as its only entry.
        T value = initialValue();
        createMap(t, value);
        return value;
    }

 

 

通过该方法获取该线程的绑定对象,具体的实现与set刚好相反

通过以上的表述,总结下threadLocalHashCode,ThreadLocal,value的关系是,

 存储方式 (entry的索引)threadLocalHashCode    (ThreadLocal,value)(entry条目)

              (entry的索引)threadLocalHashCode    (ThreadLocal,value)(entry条目)

              (entry的索引)threadLocalHashCode    (ThreadLocal,value)(entry条目)

              ..........................................................................................................

 

其实就是维持了这样一个entry数组

 

 

 threadlocal在很多框架用到,向struts2的actioncontext,hibernate的session等,

下面来说说android里面threadlocal的一个典型应用,其实就是Looper,Looper主要就是用来做异步操作的,为一个线程来不断的遍历一个消息对象队列,很显然每个Looper对象要和特定的线程做单一绑定,很显然会使用threadlocal,

下面来从一个简单的例子分析下该类内部具体对threadlocal的使用,引用该类内部提供的一个demo,代码如下

 

  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *     
  *      public void run() {
  *          Looper.prepare();
  *         
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *          //死循环,遍历消息队列,并作回调操作
  *          Looper.loop();
  *      }
  *  }

 

既然用到threadlocal,即Looper内部必有一个threadlocal变量

 

打开Looper类,确实该类内部有threadlocal变量

  private static final ThreadLocal sThreadLocal = new ThreadLocal();

 

在看看该类的prepare 方法

   public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

threadlocal很熟悉的使用方式,通过该种方式,线程可以方便的使用Looper对象做自己的异步处理.

 

 

 

 

          

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值