ThreadLocal详解
ThreadLocal简介
ThreadLocal的实例代表了一个线程局部的变量,每条线程都只能看到自己的值。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本;
它采用空间来换取时间的方式,解决多线程中相同变量的访问冲突问题。
ThreadLocal的简单使用
public class ConnectionUtils {
private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Connection getThreadConnection() {
//1.先从threddLocal中获取
Connection connection = tl.get();
//2.判断当前线程中是否有connection
if (connection == null) {
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
tl.set(connection);
}
return connection;
}
/** * 把连接和线程解绑 */
public void removeConnection() {
tl.remove();
}
}
上面的例子中,通过ThreadLocal从当前线程中获取connection,如果当前线程中没有connection,则通过数据源dataSource来创建,并放入到当前线程中;以此来保证每个线程中的connection都是互不影响的。
为什么说通过ThreadLocal来存放变量,就能保证每个线程中的变量是互相独立的呢?下面来看ThreadLocal内部实现。
ThreadLocal的实现原理
首先看ThreadLocal中的set方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
可以看到set方法中先调用了getMap(Thread t)方法,当前线程中的threadLocals;查看下方Thread类,
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
我们可以看到threadLocals为Thread类中的一个成员变量,默认值为null。
再看createMap(Thread t, T firstValue)方法,以当前threadLocal对象为key,set方法中要放入的变量为value,存入到ThreadLocalMap对象中,并赋值给当前线程的成员变量threadLocals(即以当前threadLocal对象为key,要存入的变量为value,存放到当前线程中的容器threadLocals中,类型为ThreadLocalMap)。
下面来看一下ThreadLocalMap
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;
}
}
/** * The initial capacity -- MUST be a power of two. */
private static final int INITIAL_CAPACITY = 16;
/** * The table, resized as necessary.
* table.length MUST always be a power of two. */
private Entry[] table;
ThreadLocalMap为ThreadLocal类中的一个内部类,它通过自己的内部类Entry,自己实现了一个类似Map的结构,其中可以存放多组键值对类型的数据(存放到成员变量table中)。
再看ThreadLocal中的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();
}
从上面可以看出,get方法中也是先通过当前线程获取当前线程中的ThreadLocalMap类型的成员变量threadLocals,如果不为null,再通过当前ThreadLocal对象来获取set方法中存放的value,如果为null,则返回默认值(如果没有设置默认值,则为null)。
再看ThreadLocal中的remove方法
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
remove方法时jdk1.5之后才有的方法,其目的是为了让用户可以手动删除存入到ThreadLocal中的变量(不删除也没关系,存入的变量会随着线程的销毁而销毁)
小结
其实ThreadLocal的实现就是将不能被线程共享的变量(即有状态的变量),以当前threadLocal对象为key,要存入的变量为value,存放的到当前线程中的容器threadLocals(此容器的类型ThreadLocalMap,是自己实现的一种类似Map的结构)中;因此在线程中独立,互不影响。