1.使用场景
(1)ThreadLocal适用于某些数据以线程为作用域并且不同线程具有不同数据副本的场景。
比如对于Handler来说,它需要获取当前线程的Looper,很显然Looper的作用域就是线程并且不同线程具有不同的Looper,这个时候通过ThreadLocal就可以轻松实现Looper在线程中的存取。
(2)ThreadLocal的另一个使用场景是复杂逻辑下的对象传递。
比如监听器的传递,有时一个线程中的任务过于复杂,又需要监听器贯穿整个线程的执行过程,这时就可以使用ThreadLocal,这样就可以让监听器作为线程内的全局对象而存在,在线程内部只要通过get方法就可以获取到监听器。每个监听器对象都在自己的线程内部存储。
如果不这样做,我们可能将监听器作为函数参数传递,但是这样会很麻烦,降低代码的可读性。或者我们会将监听器作为静态变量供线程访问,但是100个线程就需要100个静态的监听器对象,显然是无法接受的。
2. 使用实例
//首先定义一个ThreadLocal对象,选择泛型为Boolean类型
private ThreadLocal<Boolean> mThreadLocal = new ThreadLocal<Boolean>();
//在主线程、子线程1、子线程2中去设置访问它的值
mThreadLocal.set(true);
System.out.println("Main " + mThreadLocal.get());
new Thread("Thread#1"){
@Override
public void run() {
mThreadLocal.set(false);
System.out.println("Thread#1 " + mThreadLocal.get());
}
}.start();
new Thread("Thread#2"){
@Override
public void run() {
System.out.println("Thread#2 " + mThreadLocal.get());
}
}.start();
输出结果如下:
Main true
Thread#1 false
Thread#2 null
3. 原理介绍
不同的线程访问同一个ThreadLocal获取到的值是不一样的,这是因为呢?看一下ThreadLocal的内部实现。
3. 1 ThreadLocal的内部实现
ThreadLocal的set方法:
public void set(T value){
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if(values == null){
//初始化操作
values = initializeValues(currentThread);
}
values.put(this,value);
}
在set方法中,首先通过Thread.currentThread()获取当前线程,然后以当前线程作为参数通过一个方法构造了一个Values的实例,而这个Values是Thread类内部的一个专门用于存储线程ThreadLocal数据的成员(ThreadLocal.Values localValues),拿到Values对象实例后,它的内部又维护了一个对象数组(private Object[] table)。最初set方法传递进来的值,根据一定的算法放入table数组中。很显然不同线程中,数组是不同的,所以通过ThreadLocal可以在不同线程中维护一套数据的副本并且互补干扰。
ThreadLocal为不同线程维护了不同的变量副本,是一种空间换时间的策略,提供了一种简单的多线程的实现方式。