定义
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,存储以后,只能在该线程中可以获取到存储的数据,对于其他线程来说无法获取。
使用场景
- 某些数据以线程为作用域并且在不同线程有不同的数据时。比如每个线程要使用Handler需要创建Looper,而且每个线程的Looper是不同的,所以使用ThreadLocal可以轻松的实现Looper在线程中的存取。
- 复杂逻辑下的对象传递。比如线程中的一个监听器,它需要贯穿线程的整个生命周期,这时就可以使用ThreadLocal让监听器作为线程中的全局变量而存在。
工作原理
ThreadLocal是一个泛型类,它的定义是public class ThreadLocal< T >,只要弄清楚它的get和set方法就能明白它的工作原理。
set方法的源码:
/**
* Sets the value of this variable for the current thread. If set to
* {@code null}, the value will be set to null and the underlying entry will
* still be present.
*
* @param value the new value of the variable for the caller thread.
*/
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 values(Thread current) {
return current.localValues;
}
来得到存在Thread类里的localValues,然后调用put方法把ThreadLocal的值存储起来:
/**
* Sets entry for given ThreadLocal to given value, creating an
* entry if necessary.
*/
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;
}
}
}
由源码可知,ThreadLocal的值是存在Values的一个内部数组table里,并且存储位置是在ThreadLocal的reference对象的存储位置的下一个位置。也就是说,ThreadLocal的reference对象和值都会存储到table数组里,如果reference的位置是index,那么值的位置索引就是index+1。
get方法的源码:
/**
* Returns the value of this variable for the current thread. If an entry
* doesn't yet exist for this variable on this thread, this method will
* create an entry, populating the value with the result of
* {@link #initialValue()}.
*
* @return the current value of the variable for the calling thread.
*/
@SuppressWarnings("unchecked")
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值时,是先找到reference的索引位置,然后再加1去得到值。如果找不到当前线程的Values,那么就返回initializeValues的值,这个方法我们可以自己覆盖:
/**
* Creates Values instance for this thread and variable type.
*/
Values initializeValues(Thread current) {
return current.localValues = new Values();
}
demo
public class MainActivity extends ActionBarActivity {
private static final String TAG = "ThreadLocal";
private ThreadLocal<Integer> mThreadLocal = new ThreadLocal<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mThreadLocal.set(7);
Log.d(TAG, "In Main Thread: " + mThreadLocal.get());
new Thread(){
@Override
public void run() {
mThreadLocal.set(6);
Log.d(TAG, "In Sub Thread 1: " + mThreadLocal.get());
}
}.start();
new Thread(){
@Override
public void run() {
Log.d(TAG, "In Sub Thread 2: " + mThreadLocal.get());
}
}.start();
}
}
输出的log为:
D/ThreadLocal﹕ In Main Thread: 7
D/ThreadLocal﹕ In Sub Thread 1: 6
D/ThreadLocal﹕ In Sub Thread 2: null
由于子线程2并没有设置mThreadLocal的值,所以get得到的是null。
由此验证了同一个ThreadLocal对象在各个线程间存储的值互不干扰,因为每一个线程有自己的ThreadLocal.Values。