注:本文源码对应的是Android 29的,其他版本不能保证和其一致,本文和Java 的逻辑有所不同。
This class provides thread-local variables. These variables differ
from their normal counterparts in that each thread that accesses
one (via its {@code get} or {@code set} method) has its own,
independently initialized copy of the variable.
翻译:ThreadLocal是一个线程的局部变量。它和普通的变量不同,每个线程可以通过set和get方法访问它独立、已经初始化的变量。
1. ThreadLocal
ThreadLocal是Thread的一个局部变量,它可以保存当前线程的一些临时数据
。通过set方法设置数据,get方法获取数据。当前这两个操作一定要在线程域内触发。即便是在多个线程之间,也能保证数据的相互独立和线程安全问题
。接下来实现一个简单的实例。
1.1 ThreadLocal的简单使用
我们创建了两个线程,定义了一个ThreadLocal变量,当执行线程的时候,设置当前线程的hashcode,再次获取处理。两个线程之间保存的数据没有任何影响。
final ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
private static final String TAG = "MainActivity";
public void testThreadLocal() {
final Thread one = new Thread() {
@Override
public void run() {
super.run();
threadLocal.set("hello");
Log.d(TAG, Thread.currentThread()+"---run: ---"+threadLocal.get());
}
};
final Thread two = new Thread() {
@Override
public void run() {
super.run();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, Thread.currentThread()+"---run: ---"+threadLocal.get());
}
};
one.setName("one");
two.setName("two");
one.start();
two.start();
}
由结果可以看到,threadlocal保存的数据,外部线程去访问返回为null。这说明它保证数据的独立性。接下来我们看里面的原理。
1.2 ThreadLocal的源码分析
首先看下Thread.class类中两个变量。
- threadLocals初始化null,查找整个类,也没有赋值的地方,那会在哪里赋值呢?
- inheritableThreadLocals和threadLocals 一样,初始化null,也没有赋值的地方。
注意:这两个变量都是ThreadLocal的类里,所以咱们去ThreadLocal看看。
Thread.class
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
ThreadLocal.class
/**
* Creates a thread local variable.
* @see #withInitial(java.util.function.Supplier)
*/
public ThreadLocal() {
}
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//entry数组保存数据 ,默认大小为16,给第一位赋值,设置阈值
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
}
里面有个静态的ThreadLocalMap,这不是就是Thread里的局部变量类型。
根据名称看,它是一个容器,可能是个map形式的容器。看文档。
第一句:它是一个自定义的散列的map,适用于核心线程的本地变量。这就是保存数据的容器啊!
1.3 set方法
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//获取map对象
ThreadLocalMap map = getMap(t);
if (map != null)
//map不为null,设置数据
map.set(this, value);
else
//创建map
createMap(t, value);
}
//这里就是获取线程对象变量的地方,要不没有创建,肯定就是null
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//设置数据到一个Entry数据里,Entry是弱引用对象,能够避免内存泄漏。
//注意key是ThreadLocal ,value 是保存的内容。
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
//给线程里的变量赋值的地方。就是创建ThreadLocalmap对象。
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
1.4 get 方法
先获取当前线程对象,再通过Thread拿到ThreadLocalMap,如果不为null,通过this,也就是ThreadLocal对象,得到Entry,返回value。如果map等于null,调用setInitialValue。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private T setInitialValue() {
//返回null,下面的是不是和上述的set没有区别。
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
2.InheritableThreadLocal
这个对象是干嘛的呢,通过这个对象,我们可以在子线程里,获取到父线程的数据
(父线程的ThreadLocal保存的数据)。如果不用InheritableThreadLocal只能通过其他形式获取。 Inherit 翻译过来是继承的意思。
InheritableThreadLocal继承了ThreadLocal,且重写了三个方法,childValue() getMap() createMap()
其实就是和threadlocal类似,只是给thread类中的ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; 通过重写createMap,给它赋值,而不是thread的中threadLocal了。
2.1 使用场景
伪代码
main Thread{
final ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
threadLocal.set("main thread");
new Thread(){
run(){
//我想在这里获取主线程的保存的那个数据
system.out.println(threadlocal.get())
}
}.start();
}
2.2 InheritableThreadLocal源码分析
InheritableThreadLocal是Threadlocal的子类,它重写了几个方法,childValue、getMap、createMap.
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
protected T childValue(T parentValue) {
return parentValue;
}
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
1. childValue
字面理解,就是子项的值,可以看方法体,把父项的值拿过来然后返回,没做任何处理。怎么理解?
Thread.class
thread在构造的时候,调用了如下方法。init2()
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
// ...省略
init2(parent);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
//这里主要看ThreadLocal.createInheritedMap 创建这个map
private void init2(Thread parent) {
this.contextClassLoader = parent.getContextClassLoader();
this.inheritedAccessControlContext = AccessController.getContext();
if (parent.inheritableThreadLocals != null) {
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(
parent.inheritableThreadLocals);
}
}
回到Threadlocal的createInheritedmap方法,创建了一个对象。ThreadLocalMap这个构造干嘛了,parentmap是父线程的map对象, table = new Entry[len]; 创建一个相同长度的数组,然后遍历赋值。 Object value = key.childValue(e.value);这个方法出来了,说白了,就是从父线程拿出变量数组,复制一份给子线程inheritableThreadLocals。
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
2. getMap
获取线程对象里的变量。
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
3. createMap
当inheritableThreadLocals调用set方法,其实和threadlocals的set一样,只不过现在是子类,所以重写方法,使用的子类的功能了。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}