ThreadLocal原理解析
一、概述
ThreadLocal 的含义是线程的本地变量,每个线程都有一个自己管理的独立的引用变量,这个其实就是个map(ThreadLocalMap)这个后面说
这个主要作用在:在多线程中,而且每个线程都要有同样的初始值,但是每个线程的本地值,只能自己才能修改,其他线程不可以改,具有独立性
真正保存数据的是ThreadLoacalMap来做的,ThreadLocal只是个操作工具而已
举个例子:在Android的Looper中,为了让每个线程都有一个Looper,就是用的这个实现的ThreadLocal,
二、结构图
初始化一份ThreadLocalMap
初始化一份ThreadLocalMap
初始化一份ThreadLocalMap
ThreadLocal
A线程
B线程
C线程
三、详细分析
1、例子
ThreadLocal<String> localString=new ThreadLocal<String>()
{
@Nullable
@Override
protected String initialValue() {
return "init";
}
};
Thread thread2= new Thread(new Runnable() {
@Override
public void run() {
Log.i("localmm","local2:"+localString.get());
}
});
Thread thread1= new Thread(new Runnable() {
@Override
public void run() {
Log.i("localmm","local1:"+localString.get());
localString.remove();
Log.i("localmm","local1:"+localString.get());
localString.set("init2");
Log.i("localmm","local1:"+localString.get());
}
});
thread1.start();
thread2.start();
上面这个例子实例化了一个LocalThread 线程在调用这个对象的时候 都可以拷贝一个它的值的副本
然后自己操作自己的数据
2、分析
那么我们看下在Thread中第一次调用了localString.get() 里面做了什么
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();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
上图可以看到每个线程内部都有一个ThreadLocalMap属性,默认是null的
继续我们来看下setInitialValue()
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
上面这个是get的过程,我们可以看出来 其实每个Thread里面都一个threadLocals的变量 这个是一个Map结构的变量(ThreadLocalMap),这个ThreadLocalMap是ThreadLocal的内部静态类,而且这个map不是用的系统的map是自己实现的,我们来看下
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
private int size = 0;
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
}
ThreadLocalMap 里面有个Entry 这个entry的可以是ThreadLocal value就是保存的数据
原来在ThreadLocal 有一个threadLocalHashCode来计算map中的索引位置的具体是:
int i = key.threadLocalHashCode & (table.length - 1);
所以一个线程里面里面的threadLocalMap,可以保存很多的[ThreadLocal,Object]键值对
这个map还实现了自己扩容,索引的散列化,等
ThreadLocal里面的set和get逻辑类似,都是去每个线程的threadLocals里去操作map数据,大家可以自己去看
|