Android中ThreadLocal的工作原理
来源:互联网 发布:淘宝皮草外套新款 编辑:程序博客网 时间:2024/05/17 18:14
一,写在前面
Android中ThreadLocal有一个典型应用场景,存取主线程线程中的Looper对象,例如:在主线程中调用Looper.prepare(),与在子线程中调用Looper.prepare()初始化是不同线程的Looper对象。
那么,ThreadLocal是什么呢?ThreadLocal通常称为“线程局部变量”,也就说某些数据是以线程为作用域,在不同线程中有不同的数据副本。说到作用域,作为对比,方法里的局部变量作用域是方法体,其他方法无法访问。简单来说,希望在指定线程中存储数据,并在取出指定线程中数据,但其他线程不可访问该数据。
ThreadLocal是Java提供的原生APi,并不是Android特有的。同时Android5.0对ThreadLocal进行一些优化设计,与原生还是有区别的。本篇文章是为下篇分析Handler机制的工作流程做准备,但不准备涉及Looper相关的讲解,只对ThreadLocal在Android中表现进行分析。
二,ThreadLocal的使用示例
直接上代码,如下:
public class MainActivity extends Activity {private ThreadLocal<String> mThreadLocal;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mThreadLocal = new ThreadLocal<String>();mThreadLocal.set("ActivityThread ... ");new Thread(){public void run() {//mThreadLocal.set("new Thread() ... ");Log.e("wcc", "子线程 : " + mThreadLocal.get());};}.start();Log.e("wcc", "主线程 : " + mThreadLocal.get());}}第9行,创建ThreadLocal对象,存储数据为String类型,各个线程可以共享该ThreadLocal对象;
第10行,主线程中设置数据为"ActivityThread ... " ;
第14行,注释了,子线程不设置数据;
第15行,在子线程中获取该数据;
第19行,在主线程中获取该数据;
打印结果如下:
12-05 07:57:38.981: E/wcc(822): 主线程 : ActivityThread ...
12-05 07:57:39.001: E/wcc(822): 子线程 : null
12-05 07:57:39.001: E/wcc(822): 子线程 : null
从结果来看,在不同线程中调用ThreadLocal$get方法获取的值并不同。由于子线程没有调用ThreadLocal$set方法,取出的值是null,这是为什么呢,在后面的分析中会给出答案。
三,ThreadLocal的工作原理
下面以Android5.0的ThreadLocal的源码来分析,首先看ThreadLocal的构造函数,源码如下:
/** * Creates a new thread-local variable. */ public ThreadLocal() {}它是一个空实现,什么也没做。
ThreadLocal存储数据,会调用set方法,查看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); } //继续查看... Values values(Thread current) { return current.localValues; } //继续查看 Values initializeValues(Thread current) { return current.localValues = new Values(); }第2行,获取当前线程对象,在上述例子中指的是ActivityThread,也就是主线程;
第3行与第12行结合看,返回Thread类的localValues字段;该字段在Thread类中定义:ThreadLocal.Values localValues;
第5行,如果values为null,即localValues为null,则调用initializeValues方法;
第18行,initializeValues方法里面创建了一个Values对象,并初始化字段localValues。Values类是ThreadLocal的一个内部类,在java原生的ThreadLocal中,代替Values类的是ThreadLocalMap类。由上面可知,每一个Thread都会先初始化localValues字段,也即创建一个该线程的Values对象,每个线程的Values对象都是不同的,于是ThreadLocal可以在不同的线程中互不干扰的存储,查询数据。
第7行,调用内部类Values$put方法,存储数据。
查看ThreadLocal$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;} }}第9行,table是一个Object对象数组,定义:private Object[] table;
第11行,reference是ThreadLocal的一个字段,定义:private final Reference<ThreadLocal<T>> reference = new WeakReference<ThreadLocal<T>>(this),用弱引用对ThreadLocal进行包装。
第13,21,28行,将ThreadLocal的弱引用和需要存储的数据value放在table数组的相邻位置,形成一种映射关系,reference的索引位置加1就是value的索引位置。
从上面的分析可知,在当前的线程的Values对象中,维护了一个Object对象数组,并将ThreadLocal的弱引用与需要存储的数据,存放在数组的相邻位置。
ThreadLocal查询数据,会调用get方法,查看ThreadLocal$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); }第4行,获取当前线程的Values对象;
第8行,若ThreadLocal的弱引用在table数组中的索引位置是index,继续往下执行;
第9行,table数组索引位置为index + 1中存储了需要查询的数据,return该数据;
第12行,创建当前线程的Values对象,并赋值给Thread的字段localValues;
第15行,若当前线程的Values对象为空时,也就是未调用set方法存储值时,调用Value$getAfterMiss方法并return,下面会继续分析这里。
查看ThreadLocal$Value$getAfterMiss方法源码:
Object getAfterMiss(ThreadLocal<?> key) { Object[] table = this.table; int index = key.hash & mask; // If the first slot is empty, the search is over. if (table[index] == null) { Object value = key.initialValue(); // If the table is still the same and the slot is still empty... if (this.table == table && table[index] == null) { table[index] = key.reference; table[index + 1] = value; size++; cleanUp(); return value; } // The table changed during initialValue(). put(key, value); return value; } //...code } //继续查看... protected T initialValue() { return null; }第7行,调用ThreadLocal$initialValue方法,并赋值给变量value;
第21行,return value;
第30行,initialValue方法修饰符是protected,也就是它希望ThreadLocal子类来重写这个方法。
第31行,initialValue方法返回null;
也就是说,如果当前线程中没有调用ThreadLocal$set方法存储数据时,调用ThreadLocal$get方法查询数据会返回null。这也解释前面打印的log中,子线程为什么是null。当然,可以在创建ThreadLocal子类实例时,重写initialValue方法。
将上述示例中创建ThreadLocal实例的代码,修改如下:
mThreadLocal = new ThreadLocal<String>(){@Overrideprotected String initialValue() {return "initialValue ... ";} };运行,打印log如下:
12-05 09:32:49.111: E/wcc(7850): 主线程 : ActivityThread ...
12-05 09:32:49.131: E/wcc(7850): 子线程 : initialValue ...
12-05 09:32:49.131: E/wcc(7850): 子线程 : initialValue ...
四,最后
本篇文章先是通过一个简单的示例展示ThreadLocal的使用,可以初步了解ThreadLocal想要完成是什么样的效果。后面通过ThreadLocal的set,get方法来阐述ThreadLocal的工作原理。简单来说,ThreadLocal可以在不同线程(作用域)中,线程间互不干扰的存储和查询数据。
Android中Looper在不同线程中表现为不同的Looper对象,同时创建Handler对象时,会检查该线程中是否创建了Looper对象。那么,如何确定某一线程中是否创建Looper呢,使用ThreadLocal的特性可以很方便实现当前线程中Looper的存取操作。
值得一提的是,ThreadLocal声明的泛型是T类型,相当于Object类型。本示例中存取的String类型数据,若同时想存取其他类型的数据,需要创建一个新的ThreadLocal对象。一个ThreadLocal对象,只能存取一种类型的数据,并在不同线程中有不同的数据副本。
阅读全文
0 0
- 理解Android中ThreadLocal的工作原理
- Android中ThreadLocal的工作原理
- Android ThreadLocal工作原理
- ThreadLocal的工作原理
- ThreadLocal类的工作原理
- Android的消息机制之ThreadLocal的工作原理
- Android的消息机制之ThreadLocal的工作原理
- Android的消息机制之ThreadLocal的工作原理
- Android的消息机制之ThreadLocal的工作原理
- Android的消息机制之ThreadLocal的工作原理
- Android的消息机制之ThreadLocal的工作原理
- Android的消息机制之ThreadLocal的工作原理
- android多线程数据存储 - ThreadLocal的工作原理
- Android 消息机制之ThreadLocal的工作原理
- 深入理解----ThreadLocal的工作原理
- ThreadLocal工作原理
- ThreadLocal工作原理
- ThreadLocal工作原理
- extern关键字
- sizeof与strlen的区别
- rs.insertRow用结果集更新数据库中的表rs.updateString
- java 多线程-倒计时门栓 CountDownLatch
- 第十四周【项目1
- Android中ThreadLocal的工作原理
- PLSQL_Developer 安装配置
- 明确方向之—MYSQL DBA
- Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null objec
- 深入浅出程序化交易核心技术
- scripts(1-13) of 笨办法学Python
- 51Nod 1126 求递推序列的第N项 矩阵快速幂
- JSON数据格式
- Java中List实现类的性能分析和应用场景(基于JDK1.8)