一.关于ThreadLocal我们应该知道的
我们知道ThreadLocal被称作线程本地变量,它是一个以ThreadLocal对象为键,任意对象为值的存储结构(其实就是一个Map),这个结构被附带在线程上,也可以说是一个线程根据ThreadLocal对象查询绑定到这个线程上的一个值。
that accesses one (via its {@code get} or {@code set} method) has its own, independently initialized copy of the variable. {@code ThreadLocal} instances are typically private static fields in classes that wish to associate state with a thread (e.g.,a user ID or Transaction ID)
1
上面这句英语是官网给的解释,翻译过来如下:ThreadLocal类用来提供线程内部的局部变量,这些变量在多线程访问的情况下(通过get()或set()方法访问)能保证各个线程里的变量相对独立于其他线程里的变量,ThreadLocal通常都是private static类型。
总结:ThreadLocal是线程为自己存储某一个变量(或者说对象)而提供的一种手段,提供了保持对象的方法和避免参数传递的复杂性
二.ThreadLocal源码解析
1.ThreadLocal的源码较多,以下是ThreadLocal类里主要的一些变量和静态内部类:
public class ThreadLocal<T> {
private final int threadLocalHashCode = nextHashCode();
//AtomicInteger类可以线程安全的进行加减操作
private static AtomicInteger nextHashCode =
new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
//getAndAdd方法就是AtomicInteger类中的方法,可以线程安全的将原来的值加上一个给定的增量
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
//ThreadLocalMap是ThreadLocal的静态内部类
static class ThreadLocalMap {
//Entry又是ThreadLocalMap的静态内部类
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
事实上ThreadLocal里面的大部分东西都是通过静态内部类实现的,它的set方法,get方法,remove方法等的实现都是在静态内部类中实现,我们将在下面的源码中看见
2.我们可以用ThreadLocal中的set方法保存一个值,也就是设置当前线程的本地变量的值。
public void set(T value) {
//得到当前线程
Thread t = Thread.currentThread();
//调用getMap方法,为了方便,将getMap方法放在下面
ThreadLocalMap map = getMap(t);
//判断是否为空
if (map != null)
//这里需要注意key是this,而不是很多人说的线程的名字或者标识
map.set(this, value);//调用静态内部类的set方法
else
createMap(t, value); //如果为空,就先创建一个Map
}
-----------------------------------------------------
参数t表示当前线程,所以返回的就是当前线程的threadLocals
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
------------------------------------------------------
//一个ThreadLocalMap类型的threadLocals变量,初始值为null
ThreadLocal.ThreadLocalMap threadLocals = null;
3.当我们set一个值以后,可以通过get方法取到这个值。
public T get() {
//得到当前线程
Thread t = Thread.currentThread();
//同样是调用getMap方法,得到当前线程的Map
ThreadLocalMap map = getMap(t);
if (map != null) {
//调用getEntry方法,getEntry方法在下边
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
//该批注的作用是给编译器一条指令,告诉它对被批注代码里的某些警告保持静默
@SuppressWarnings("unchecked")
//拿到这个key对应的value
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
-----------------------------------------------------------------
private Entry getEntry(ThreadLocal<?> key) {
//计算key在数组中的位置
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
//判断取到的key不为空,并且数组中返回的key与参数key是否相等
if (e != null && e.get() == key) //get方法是静态内部类中的方法
return e;
else
return getEntryAfterMiss(key, i, e); // 这个方法最后返回的会是null
}
4.如果我们想移除这个值,可以用remove方法,remove方法源码如下:
这是ThreadLocal里面的remove方法
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
调用下面的remove方法
m.remove(this);
}
----------------------------------------------------------------
//这是静态内部类Entry里的remove,remove方法的主要逻辑也在这里面
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
//计算key在数组中的位置,在前面的get方法也有这一步
int i = key.threadLocalHashCode & (len-1);
//遍历数组找到这个key
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
//清除
e.clear();
expungeStaleEntry(i);
return;
}
}
}
三.ThreadLocal的应用
ThreadLocal的应用非常广泛,在spring和Hibernate框架作用非常大,还有Sesssion管理之类的。比如:每用户登陆网站,用户服务器都需要为其创建一个线程,我们可以将用户的信息存储在线程的ThreadLocal中,在页面跳转时,新页面可能需要一些用户信息,我们可以在ThreadLocal中取得。
四.总结
通过上面的分析,我们可以知道ThreadLocal通过静态内部类ThreadLocalMap实现,ThreadLocalMap是一个Map,当调用set方法时,这个map的key值,也就是this其实就是一个ThreadLocal类型的变量(线程自己的变量)。因此这个变量对应的value也只能由这个线程自己可以取到,因此我们可以说每一个线程独自享有一个变量,与其他线程无关。
因此ThreadLocal虽然是用来解决多线程问题的,但是它只局限于自己的这个线程,ThreadLocal变量的活动范围为某线程,对该变量的所有操作均由该线程完成。