已经有几个月没有写博客了,虽然作为一名菜鸟,但是学习还是要持之以恒的,多写写博客,总结一下自己的学习成果还是不错的。今天,我们就一起来学习一下ThreadLocal吧!
说起ThreadLocal,应该大家都觉得有点陌生。确实,在日常的开发中,ThreadLocal用的地方比较少,但是在某些场景下,它是可以完成复杂的功能。例如Android消息机制,Handler和Looper。Handler工作的时候,需要获取Looper,但是Looper的作用域是线程,而且不同的线程具有不同的Looper。这个时候,使用ThreadLocal就比较方便了,因为它是一个线程内部的存储类,它可以在指定线程中存储数据,而对于其他线程来说则是无法获取的。我们来看看一个例子:
private ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("000000");
System.out.println("Thread#main ThreadLocal=" + threadLocal.get());
new Thread("Thread#1"){
@Override
public void run() {
threadLocal.set("111111");
System.out.println("Thread#2ThreadLocal=" + threadLocal.get());
}
}.start();
new Thread("Thread#2"){
@Override
public void run() {
threadLocal.set("222222");
System.out.println("Thread#2ThreadLocal=" + threadLocal.get());
}
}.start();
在上面的例子中,算上主线程,一共开启的三个线程,在每个线程里面都有一个共同的ThreadLocal,而且也进行了数据的存储和获取,看看结果是怎样的:
每个线程之间的ThreadLocal值没有互相影响,这是为什么呢?我们从源码上去解析。
ThreadLocal是一个泛型类,他的定义是:
public class ThreadLocal<T>
我们首先看看它的set()方法:
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
currentThread获取的是当前线程的实例,接着出现了一个Values的类,其实这个就是ThreadLocl的静态内部类static class Values,LocalThread.Values。
接着就会调用values()方法,这个方法究竟是做干什么的?我们看看它的代码:
Values values(Thread current) {
return current.localValues;
}
原来values()方法就是获取当前线程的ThreadLocal数据,因为在Thread类的内部,有一个成员专门用于存储线程的ThreadLocal的数据:
ThreadLocal.Values localValues;
恰好就是ThreadLocal的静态内部类Values, 通常这个值默认是为null的,所以在一开始的时候会进行初始化工作initializeValues()。
那么如何将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;
}
}
}
这里面我们算法不用去管,但是我们能看出的就是ThreadLocal的值是存储在table[]数组中,而且是存储在ThreadLocal的reference字段所标识的下一个位置。假如ThreadLocal的reference在table[]中的下标是index,那么ThreadLocal的值就是存储在table[index+1]=value。
现在再分析它的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就是比较清晰的,获取当前线程的ThreadLocal.Values对象,再获取values里面的table[]数组的table[index+1]的值。
总结:
ThreadLocal的存储原理,其实就是获取当前线程的localValues对象中的table[]数组,不同的线程,localValues是不同的,所以他就能在多线程中互不干扰地存储数据和修改数据。