ThreadLocal是什么
ThreadLocal一般称为线程本地变量,它是一种特殊的线程绑定机制,将变量与线程绑定在一起,为每一个线程维护一个独立的变量副本。通过ThreadLocal可以将对象的可见范围限制在同一个线程内。
ThreadLocal用法和原理
ThreadLocal提供了4个公共的方法
1、ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。
2、ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值。
3、ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。
4、ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程调用remove方法后调用get方法,返回此方法值。
我们通过Handler的源码来分析下ThreadLocal的使用和原理,上代码:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
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));
}
在handler中的上述两个方法中,使用了ThreadLocal的get和set方法,这也就是为什么能够保证一个线程中始终只有一个Looper对象,这样防止了数据的脏读,即防止当前线程访问到其他线程的数据。我们先从set方法的源码看起
public void set(T value) {
Thread t = Thread.currentThread();
//通过当前线程获取ThreadLocalMap ,
ThreadLocalMap map = getMap(t);
if (map != null)
//map的key是ThreadLocal,value为Looper
map.set(this, value);
else
//map为null时创建map
createMap(t, value);
}
先来看看getMap()方法做了什么
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
上述代码可以看出,在Thread中维护了一个 ThreadLocal.ThreadLocalMap对象
再来看createMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
上述代码不需解释,接下来看看get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
//在set方法中我们看到,map的key是ThreadLocal,所以这里通过ThreadLocal来获取map中的value,这行代码可以看出Looper被包装在了ThreadLocalMap.Entry中
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
//Entry中的value即为Looper
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
如果map为null,看这里setInitialValue
private T setInitialValue() {
//这里即是ThreadLocal没有被当前线程赋值时或当前线程调用remove方法后调用initialValue
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
这里总结一下Thread,ThreadLocal和ThreadLocalMap三者之间的关系
一个Thread中只有一个ThreadLocalMap,一个ThreadLocalMap中可以有多个ThreadLocal对象(ThreadLocal为map的key),其中一个ThreadLocal对象对应一个ThreadLocalMap中的一个Entry。
接下来我们看下ThreadLocalMap.Entry
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是静态内部类,这样的好处的是防止持有外部类的引用而引起的内存泄漏,接着Entry 继承了WeakReference,即弱引用对象,将map的key即ThreadLocal对象变成一个弱引用的对象
最后我们看看ThreadLocalMap底层是什么数据结构
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//是一个初始长度为16的Entry数组
table = new Entry[INITIAL_CAPACITY];
//自己实现了如何从 key 到 value 的映射
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
使用一个 static 的原子属性 AtomicInteger nextHashCode,通过每次增加 HASH_INCREMENT = 0x61c88647 ,然后 & (INITIAL_CAPACITY - 1) 取得在数组 private Entry[] table 中的索引。
public class ThreadLocal<T> {
/**
* ThreadLocals rely on per-thread linear-probe hash maps attached
* to each thread (Thread.threadLocals and
* inheritableThreadLocals). The ThreadLocal objects act as keys,
* searched via threadLocalHashCode. This is a custom hash code
* (useful only within ThreadLocalMaps) that eliminates collisions
* in the common case where consecutively constructed ThreadLocals
* are used by the same threads, while remaining well-behaved in
* less common cases.
*/
private final int threadLocalHashCode = nextHashCode();
/**
* The next hash code to be given out. Updated atomically. Starts at
* zero.
*/
private static AtomicInteger nextHashCode =
new AtomicInteger();
/**
* The difference between successively generated hash codes - turns
* implicit sequential thread-local IDs into near-optimally spread
* multiplicative hash values for power-of-two-sized tables.
*/
private static final int HASH_INCREMENT = 0x61c88647;
/**
* Returns the next hash code.
*/
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
上述key至value的映射作者也不甚明了,简单说来ThreadLocalMap是一个类似HashMap的集合,只不过自己实现了寻址,也没有HashMap中的put方法,而是set方法等区别。