ThreadLocal介绍

   ThreadLocal如果单纯从字面上理解的话好像是“本地线程”的意思,其实并不是这个意思,只是这个名字起的太容易让人误解了,它的真正的意思是线程本地变量。看看官方怎么说的。

实现一个线程本地的存储,也就是说,每个线程都有自己的局部变量。所有线程都共享一个ThreadLocal对象,但是每个线程在访问这些变量的时候能得到不同的值,每个线程可以更改这些变量并且不会影响其他的线程,并且支持null值。


private ThreadLocal<Boolean>mBooleanThreadLocal = new ThreadLocal<Boolean>();
public void testThreadLocal(){
    mBooleanThreadLocal.set(true);
    Log.d(TAG, "[Thread#main]mBooleanThreadLocal=" + mBooleanThreadLocal.get());

    new Thread("Thread#1") {
        @Override
        public void run() {
            mBooleanThreadLocal.set(false);
            Log.d(TAG, "[Thread#1]mBooleanThreadLocal=" + mBooleanThreadLocal.get());
        };
    }.start();

    new Thread("Thread#2") {
        @Override
        public void run() {
            Log.d(TAG, "[Thread#2]mBooleanThreadLocal=" + mBooleanThreadLocal.get());
        };
    }.start();
}


在上面的代码中,在主线程中设置mBooleanThreadLocal的值为true,在子线程1中设置mBooleanThreadLocal的值为false,在子线程2中不设置mBooleanThreadLocal的值,然后分别在3个线程中通过get方法去mBooleanThreadLocal的值,根据前面对ThreadLocal的描述,这个时候,主线程中应该是true,子线程1中应该是false,而子线程2中由于没有设置值,所以应该是null,安装并运行程序,日志如下所示:

D/TestActivity(8676):[Thread#main]mBooleanThreadLocal=true

D/TestActivity(8676):[Thread#1]mBooleanThreadLocal=false

D/TestActivity(8676):[Thread#2]mBooleanThreadLocal=null

总结:


ThreadLocal 不是用于解决共享变量的问题的,不是为了协调线程同步而存在,而是为了方便每个线程处理自己的状态而引入的一个机制。

所以ThreadLocal既不是为了解决共享多线程的访问问题,更不是为了解决线程同步问题,ThreadLocal的设计初衷就是为了提供线程内部的局部变量,方便在本线程内随时随地的读取,并且与其他线程隔离。





Android2个地方采用了

1).Android的消息机制主要是指Handler的运行机制

一般来说,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal。比如对于Handler来说,它需要获取当前线程的Looper,很显然Looper的作用域就是线程并且不同线程具有不同的Looper,



2).开源框架EventBus存储当前线程下的发送事件队列状态也是采用ThreadLocal




ThreadLocal使用场景


1.)当某些数据以线程为作用域,并且不同线程拥有不同数据副本的时候。

   ThreadLocal使用场合主要解决多线程中数据因并发产生不一致的问题。ThreadLocal以空间换时间,为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,但大大减少了线程同步所带来的线程消耗,也减少了线程并发控制的复杂度。

   例如Android的Handler消息机制,对于Handler来说,它需要获取当前线程的looper很显然Looper的作用域就是线程并且不同线程具有不同的Looper,这个时候通过ThreadLocal就可以轻松实现Looper在线程中的存取。再例如开源框架EventBus,EventBus需要获取当前线程的PostingThreadState对象,不同的PostingThreadState同样作用于不同的线程,EventBus可以很轻松的获取当前线程下的PostingThreadState对象,然后进行相关操作。

2.)复杂逻辑下对象传递,比如监听器的传递

   使用参数传递的话:当函数调用栈更深时,设计会很糟糕,为每一个线程定义一个静态变量监听器,如果是多线程的话,一个线程就需要定义一个静态变量,无法扩展,这时候使用ThreadLocal就可以解决问题。

原理:等同于

ConcurrentMap<Thread, Object>,比它好,安全

从使用者的角度而言,一般我们可以将ThreadLocal看做是一个:ConcurrentHashMap<Thread, Object>,以Thread引用为key, 来保存本线程的局部变量。但是从实现的角度而言,ThreadLocal的实现根本就不是这样的。下面从源码分析ThreadLocal的实现。


set方法:通过Map绑定线程




public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}




get方法:通过键得到值