ThreadLocal

提供了保持对象的方法和避免参数传递的对象访问方式

常用方法

get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public T get() {
//获得当前线程
Thread t = Thread.currentThread();
//获得map
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();
}

getMap方法返回当前线程threadLocals属性

1
2
3
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}

threadLocals为ThreadLocalMap类型,被定义在Thread类当中,默认为null

1
2
3
4
5
public class Thread implements Runnable {
//...
ThreadLocal.ThreadLocalMap threadLocals = null;
//...
}

ThreadLocalMap为ThreadLocal中定义的静态内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ThreadLocal<T> {
//...
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
//存储的值
Object value;
//以ThreadLocal为键
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//其他属性..
}
//...
}

map为空的时候,调用setInitialValue方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//map不为空,就设置键值对,为空,再创建Map
private T setInitialValue() {
//赋值为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;
}
//默认返回null
protected T initialValue() {
return null;
}

创建Map的方法

1
2
3
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

set

1
2
3
4
5
6
7
8
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

存取原理

每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,其键值为当前ThreadLocal变量,value为要存储的数据

初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,
并且以当前ThreadLocal变量为键值,要保存的数据为value,存到threadLocals

然后在当前线程里面,就可以通过get方法在threadLocals里面查找

典型应用

hibernate中典型的ThreadLocal的应用:Session管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() throws InfrastructureException {
//这个session相当于线程的私有变量,各个线程只能取到自己的ThreadLocalMap中的东西
Session s = (Session) threadSession.get();
try {
if (s == null) {
//当前线程没有session,则创建一个session,再set到线程中
//实际是放到当前线程的ThreadLocalMap这个map中
s = getSessionFactory().openSession();
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}

注意事项

ThreadLocal不是用来解决共享对象的多线程访问问题的,
通常,通过ThreadLocal.set()到线程中的对象是该线程自己使用的对象,其他线程无法访问

ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,
而是通过每个线程中的new对象 的操作来创建的对象,每个线程创建一个,不是对象的拷贝或副本,

通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,
每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,
因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的

总结

每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。

将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,
然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递。