1.threadLocal官方解释是线程本地变量意思。从实际应用来看,Threadlocal主要存储关键资源等(例如:Session,Connection)。ThreadLocal内部通过ThreadLocalMap进行存储,其中key是threadlocal,value是存储的资源对象。ThreadLocalMap是ThreadLocal的静态内部类,ThreadLocal内部还有一个静态内部类Entry主要是构造数据。
2.ThreadLocal中最重要的3个方法:
2.1一个是initialValue()这个是受保护的方法,使用时需要重新覆盖。
2.2 get():通过ThreadLocal的get()方法获取当前线程的对应的value对象。
2.3set(T value):将当前value和当前线程对象存储到ThreadLocal的ThreadLocalMap中。
2.4remove():删除当前线程对象绑定的对象资源
ThreadLocal中initialValue方法返回值为null,开发人员在必要时需要进行重写。可以通过匿名内部类方式进行重写。
protected T initialValue() {
return null;
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
1. 使用ThreadLocal存储进行存储对象时,set()首先根据当前线程对象去获取ThreadLocalMap对象实例。如map对象实例存在,则将当前值value对象存入到ThreadLocalMap中。如果是第一次存入,则需要创建这个ThreadLocalMap对象。通过createMap方法。下面逐个解释:
1.1Thread t = Thread.currentThread():获取当前线程的对象
1.2 ThreadLocalMap map = getMap(t);根据当前线程对象实例获取ThreadLcoalMap对象,这个map是真正存储资源对象的。
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
1.2.1通过getMap方法可以看出,该方法只是返回Thread的一个类型为ThreadLocalMap的变量。在Thread类中有一个类型为ThreadLocal.ThreadLocalMap的变量threadLocals。
1.3如果ThreadLocalMap不为空,则通过map.set(key,value)方法。即 map.set(this, value)进行存储当前线程绑定的资源对象。其中key值为当前threadLocal对象实例,这是因为一个线程中可能会有多个ThreadLocal变量。下面我们可以看看ThreadLocalMap这个内部类:
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
1.3.1 ThreadLocalMap这个内部类中还有一个内部类Entry。这个内部类的构造函数中参数为ThreadLocal,和值为Object类型的。最终ThreadLocalMap中的key,value是以Entry对象实例形式存储。因为第一次存储时实例化ThreadLocalMap对象时以这种方式存储(下文讲解)。
1.4 如果getMap(t)得到的ThreadLocalMap为空,则执行createMap(t, value);这条语句。可以看看源码:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
/***
* Construct a new map initially containing *(firstKey, firstValue).
*ThreadLocalMaps are constructed lazily, so we only create
- one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
1.4.1createMap()方法中,第一次实例化ThreadLocalMap时指定当前ThreadLocal实例为key,value为需要存储的对象。其中key,value作为类Entry构造参数,同时存储到Entry[]类型数组中。
get()方法
1.通过get方法获取当前线程对象绑定的资源对象。如果是第一次获取,则需要重写initialValue()方法。源码如下:
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
源码中获取当前线程绑定的资源对象变量,就是通过getMap(t)获取当前线程对象的ThreadLocalMap实例,然后从map中获取根据key-ThreadLocal获取所需要内容。
如果当前map对象不存在,则通过setInitialValue()方法获取。这种情况属于第一次将资源对象与ThreadLocal进行绑定。
/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
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;
}
上述源码中,initialValue()方法默认返回null,需要开发人员覆盖该方法。如果是第一次绑定执行到createMap方法中。
remove()方法
remove方法主要是将当前线程对象与绑定的资源对象的key-value关系解除,并不是真正销毁对象。可以看看源码:
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocal实际应用
ThreadLocal不是解决资源同步问题的,主要是解决多线程环境下线程非安全资源的使用。通过ThreadLocal将每个线程都绑定一个共享资源的”副本”,我觉得副本这个词不是很准确。比如在Spring中,数据库连接connection的管理就是通过ThreadLocal解决的。在Spring中的DataUitls类中有如下:
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(dataSource.getConnection());
}
return conHolder.getConnection();
}
// Else we either got no holder or an empty thread-bound holder here.
logger.debug("Fetching JDBC Connection from DataSource");
Connection con = dataSource.getConnection();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
logger.debug("Registering transaction synchronization for JDBC Connection");
// Use same Connection for further JDBC actions within the transaction.
// Thread-bound object will get removed by synchronization at transaction completion.
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
holderToUse = new ConnectionHolder(con);
}
else {
holderToUse.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}
return con;
}
1.这是Spring中DataUtils类中获取connection的方法,其中Connection通过ConnectionHolder管理,而ConnectionHolder是通过TransactionSynchronizationManager获得的。就是上文中的第一句代码:
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
2.TransactionSynchronizationManager这个类中封装了很多ThreadLocal变量。
3.上述代码中,如果获得ConnectionHolder的对象为空,则通过dataSource.getConnection()方法获得数据库连接,然后构造ConnectionHolder对象实例holderToUse = new ConnectionHolder(con);
,将holderToUse和dataSource作为适配器ConnectionSynchronization的参数注册到TransactionSynchronizationManager中的synchronizations中。
最后将holderToUse与dataSource作为Map对象存储到ThreadLocal中。具体源码如下:
public static void bindResource(Object key, Object value) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Assert.notNull(value, "Value must not be null");
Map
ThreadLocal总结
学习ThreadLocal主要是理解掌握ThreadLocal的思想和设计方法,并灵活运用。