ThreadLocal是一个线程内部的数据存储类,通过它可以在同一个线程内共享数据。
ThreadLocal原理
ThreadLocal代码分析
从demo说起
/*
* 持有ThreadLocal的类
**/
public class ThreadLocalContext {
private static final ThreadLocal<String> NAME_LOCAL = new ThreadLocal<>();
private static final ThreadLocal<Integer> AGE_LOCAL = new ThreadLocal<>();
public static void setName(String name) {
NAME_LOCAL.set(name);
}
public static void setAge(Integer age) {
AGE_LOCAL.set(age);
}
public static String getName() {
return NAME_LOCAL.get();
}
public static Integer getAge() {
return AGE_LOCAL.get();
}
}
//测试代码
public static void main(String[] args) {
new Thread(()->{
ThreadLocalContext.setAge(1);
ThreadLocalContext.setName("test");
print();
}).start();
new Thread(()->{
ThreadLocalContext.setAge(10);
ThreadLocalContext.setName("xixi");
print();
}).start();
}
private static void print(){
String desc=ThreadLocalContext.getName()+"-"+ThreadLocalContext.getAge();
System.out.println(Thread.currentThread().getName()+":"+desc);
}
根据上面的demo代码,我们就从get/set进行分析ThreadLocal的代码
从上面代码中可以看到,ThreadLocalContext持有了两个ThreadLocal对象NAME_LOCAL
和AGE_LOCAL
。
ThreadLocal.set()
我们首先从setName和setAge说起。
public class ThreadLocal{
//set 方法
public void set(T value) {
Thread t = Thread.currentThread();//1.获取当前线程
ThreadLocalMap map = getMap(t);//2.从当前线程中获取到ThreadLocalMap
if (map != null)
map.set(this, value); //3.如果不为空,将当前的ThreadLocal对象为key,存储到当前线程的ThreadLocalMap中
else
createMap(t, value); //4.如果为空,需要为当前Thread创建ThreadLocalMap对象
}
}
可以看到上面的代码,为什么可以隔离每个线程的数据。
就是因为ThreadLocalMap是Thread的成员变量,所以数据是存储到Thread对象里的,别的Thread是没办法获取到其他线程的对象。
ThradLocal.get()
public class ThreadLocal{
public T get() {
Thread t = Thread.currentThread(); //1.获取当前线程
ThreadLocalMap map = getMap(t); //2.通过当前线程获取ThreadLocalMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); //3.根据当前的ThreadLocal为key,获取Entry对象
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();//4. 初始化ThreadLocalMap
}
static class ThreadLocalMap{
//从上面步骤3过来
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[]存储ThreadLocal对象,每个ThreadLocal都有一个hashCode,每创建一个ThreadLocal其对应的hashCode会加上0x61c88647
,以保证散列足够的散。
即使这样,仍然会出现hash冲突的情况,所以在getEntry的方法中,存在获取到entry会当前ThreadLocal不一致的情况,此时会继续执行getEntryAfterMiss
方法。
如果再去分析源码的话,会看到Entry实际上是继承了WeakReference,来标识引用的ThreadLocal为弱引用,这样能够保证,当ThreadLocal没有其他引用的时候,能够正常被垃圾回收,不会因为Entry的引用,而无法回收。
网友评论