ThreadLocal使用和源码分析
2017-02-13 17:25
483 查看
1.ThreadLocal是什么?
从名字可以看出来是线程局部变量的意思。ThreadLocal的功能非常简单,就是为每一个访问该变量的线程创建一个副本,使每一个线程可以独立的改变自己线程的副本,而不会和其他线程的副本有冲突。从线程的角度来看,就好像每个线程都有这个变量,当线程消失时,该副本也会被扔到GC中等待垃圾回收。2.ThreadLocal API介绍
initialValue方法protected T initialValue() { return null; }
initialValue()方法是用来初始化变量值的。默认此方法返回的是null,如果需要null以外的初始值则需要复写此方法,返回需要的值。
一般来说在线程中此方法只会被调用一次,在第一次调用get()方法时调用了此方法,如果之前调用过set()方法,则不会再次调用该方法。但是如果之后调用了remove()方法,则之后再次使用时可能会再次调用该方法。
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); }
get()方法返回该变量的值,通过代码可以看出首先获取当前线程对象,然后返回当前对象对应的values值,如果values为null,则电泳initializeValues(currentThread)方法创建该对象。
Values initializeValues(Thread current) { return current.localValues = new Values(); }
然后调用getAfterMiss(this)方法。
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) { //如果数组对应值为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; } // Keep track of first tombstone. That's where we want to go back // and add an entry if necessary. int firstTombstone = -1; // Continue search. for (index = next(index);; index = next(index)) { Object reference = table[index]; if (reference == key.reference) { return table[index + 1]; } // If no entry was found... if (reference == null) { Object value = key.initialValue(); // If the table is still the same... if (this.table == table) { // If we passed a tombstone and that slot still // contains a tombstone... if (firstTombstone > -1 && table[firstTombstone] == TOMBSTONE) { table[firstTombstone] = key.reference; table[firstTombstone + 1] = value; tombstones--; size++; // No need to clean up here. We aren't filling // in a null slot. return value; } // If this slot is still empty... if (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; } if (firstTombstone == -1 && reference == TOMBSTONE) { // Keep track of this tombstone so we can overwrite it. firstTombstone = index; } } }
可以看到在该方法中做了判断然后调用初始化方法返回初始值。
如果values不为null,则直接返回其中的值。
set()方法
public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); }
该方法最后调用了put()方法,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; } } }
put()方法实现了存储的过程,其实就是将变量值存入到Object数组中。
remove()方法
public void remove() { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { values.remove(this); } } 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; } } }
移除此线程当前线程变量的值。
3.ThreadLocal与线程同步的区别
同步锁采用以时间换空间的方式来操作变量,ThreadLocal采用以空间换时间的的方式操作变量Java中synchronized是JVM的保留字,通过JVM的锁机制确保临界区的函数或变量的原子性。在同步机制中通过锁机制确保变量或方法在同一时间只能被一个线程操作,此时锁对象是被多个线程共享的。而ThreadLocal会为每一个线程创建一个和该线程绑定的副本,线程只需要操作该副本就可以了。因为线程操作的是本线程的副本对象,所以没有必要进行加锁了。
本质上来说,同步锁机制是为了同步多个线程对一个对象操作的并发问题,一个资源被多个线程共享,多个线程通过该变量进行通信。而ThreadLocal从本质上讲是隔离线程。所以如果线程之间需要进行通信,则使用同步锁机制,如果需要隔离多线程之间的对共享资源的冲突则采用ThreadLocal。
4.ThreadLocal在Android中的使用
在Android中典型的使用就是Handler使用ThreadLocal,在Android源码中,在创建Looper时,我们知道Looper是需要和当前线程进行绑定的private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
可以看到通过ThreadLocal的set()方法设置了Looper对象,通过之前的分析我们知道set()方法是实现绑定的线程和对象的,这样就完成了Looper与当前线程的绑定。
QQ交流群
![](http://img.my.csdn.net/uploads/201709/08/1504853209_3603.png)
微信公众号:Android在路上,欢迎关注
![](http://img.my.csdn.net/uploads/201709/08/1504853083_7868.jpg)
相关文章推荐
- Spring源码分析【6】-ThreadLocal的使用和源码分析
- 分析balde源码,查看Web工程处理Ioc注入的背后的过程,ThreadLocal 使用保存线程所有的request and respond,
- lesson1:threadlocal的使用demo及源码分析
- Java并发编程系列(六)---- ThreadLocal使用及源码分析
- ThreadLocal基本使用和源码分析
- ThreadLocal源码分析与使用场景
- ThreadLocal使用及源码分析
- 蔡军生先生第二人生的源码分析(六十七)LLXMLNode使用Expat库打开文件
- 第二人生的源码分析(四十一)使用Apache运行库线程
- 第二人生的源码分析(四十一)使用Apache运行库线程
- 蔡军生先生第二人生的源码分析(五十八)使用FreeType字体
- 第二人生的源码分析(六十七)LLXMLNode使用Expat库打开文件
- 第二人生的源码分析(六十七)LLXMLNode使用Expat库打开文件
- .NET / Rotor源码分析5 - 开始使用WinDbg+SOS调试,sscoree.dll,加载SOS并设置JIT断点
- 第二人生的源码分析(六十九)使用LLXmlTree类来分析XML配置文件
- 第二人生的源码分析(六十九)使用LLXmlTree类来分析XML配置文件
- 第二人生的源码分析(六十八)LLXMLNode使用Expat库分析XML文件
- 第二人生的源码分析(四十一)使用Apache运行库线程
- 第二人生的源码分析(六十七)LLXMLNode使用Expat库打开文件
- 第二人生的源码分析(五十八)使用FreeType字体