ThreadLocal用于保存某个线程共享变量,对于同一个ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
源码分析
核心方法:
1、ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。
2、ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值。
3、ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。
4、ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值。
先看看源码ThreadLocal:
//由类的定义可以看出,这里传进去了一个泛型,这里代表ThreadLocal中所操作的数据可以是任何类型
public class ThreadLocal<T> {
//默认构造方法
public ThreadLocal() {
}
//初始值为空,使用时可以重写该方法
protected T initialValue() {
return null;
}
//这里创建初始值,ThreadLocalMap是在ThreadLocal中声明,在Thread中定义的一个变量,默认值为null,
//可以看出,设置的是当前线程currentThread()的一个内部属性变量ThreadLocalMap
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;
}
//这里也是到线程currentThread()里面去取值,key为当前线程ThreadLocal
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();
}
//c这里也是到线程currentThread()里面设置,key为当前线程ThreadLocal
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
//移除
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
//创建,这个方法是在当前线程第一次储存变量的时候调用
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//获取,默认Thread中ThreadLocalMap threadLocals = null
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
}
Thread 中ThreadLocalMap 定义如下:
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
}
这里不难看出:
- 这里当前线程中有一个
ThreadLocalMap threadLocals
属性的东西,用来存储当前线程的私有数据。 ThreadLocal
的实例代表了一个当前线程局部的变量,每条线程都只能看到自己的值,并不会意识到其它的线程中也存在该变量。
现在唯一不了解的就是 ThreadLocal
中定义的ThreadLocalMap
数据结构。下面来看看:
public class ThreadLocal<T> {
//当前ThreadLocal存储在ThreadLocalMap数组中的坐标位置,通过hash值及相关算法生成
private final int threadLocalHashCode = nextHashCode();
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//ThreadLocalMap 实际存储数据的数组
private Entry[] table;
//初始容量
private int size = 0;
//获取ThreadLocal
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 用来管理 ThreadLocal,
- ThreadLocalMap 数据结构为一个不连续的数组。
所以,综上就连起来了,ThreadLocal为当前线程Thread的私有数据,Thread通过可变数组ThreadLocalMap来管理ThreadLocal。开发者可以直接通过
ThreadLocal
来操作当前线程的私有数据。
应用
多数据源的应用场景,主要针对跨多个MySQL实例的情况。利用 注解 + AOP + ThreadLocal来
实现,这里给出应用到ThreadLocal的部分如下:
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
* 动态数据源
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}