首先举例说明ThreadLocal类的使用:
public class ThreadTest {
static ThreadLocal<String> tLocal = new ThreadLocal<String>();
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
tLocal.set(Thread.currentThread().getName());//将main线程名称保存到tlocal中
//使用匿名类创建一个新线程
Thread thread = new Thread(){
public void run(){
tLocal.set(Thread.currentThread().getName());
System.out.println(tLocal.get());
}
};
thread.start();
thread.join();
System.out.println(tLocal.get());
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
运行结果:
Thread-0
Main
根据运行结果可知,在threadlocal中为每个线程存储一个单独的值。下面,结合上面例子,分析源码具体了解该类的原理:
首先threadlocal实例化,查看构造函数,没有任何其它操作。
public ThreadLocal() {
}
Set()方法理解:
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//ThreadLocalMap是ThreadLocal的静态内部类,具体见下面getMap方法,
//第一次执行该方法,map是null,
ThreadLocalMap map = getMap(t);
if (map != null)
//具体见下面set方法
map.set(this, value);
else
//当map为null是,创建map,具体见下面的createMap源码
createMap(t, value);
}
//在Thread类中,定义了threadLocals变量
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap.set()方法:
private void set(ThreadLocal key, Object value) {
//获取类型为Entry的table数组,Entry是ThreadLocalMap的内部类
Entry[] tab = table;
int len = tab.length;
//根据key的hash值与15做&运算,得出下标
int i = key.threadLocalHashCode & (len-1);
//循环,当e不为null,执行循环体
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//实例化Entry,赋给tab[i],其实threadlocal本质就是将每个线程中的
//theadlocal.threadlocalmap对象和共享的变量,形成entry对象,
tab[i] = new Entry(key, value);
int sz = ++size;
//当table数组中的元素大于10,扩展table;即每个线程中的threadlocal //数量大于10时,会对table数组扩展
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
ThreadLocal.createMap(Thread t,T firstValue)方法:
//为每个线程创建ThreadLocalMap实例,
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//ThreadLocalMap构造函数,传入
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
//创建Entry类型的数组,长度为16
table = new Entry[INITIAL_CAPACITY];
//获取存放Entry实例的下标
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
//设置扩展table长度的阈值
setThreshold(INITIAL_CAPACITY);
}
以上分析代码可知,ThreadLocal在多线程中的应用中的原理如下图:
根据图可知,每个线程中,都有自己独立的了ThreadLocalMap类的实例,ThreadLocalMap中的key是tLocal对象的引用,value表示每个线程中的独立的变量值,ThreadLocal的set方法的实质就是:若当前线程中ThreadLocalMap为null,则实例化ThreadLocalMap,同时传入ThreadLocal的引用和变量值;若不为null,执行ThreadLocalMap的set方法,同样需要传入ThreadLocal的引用和变量值。
当需要获取变量值时,调用ThreadLocal的get方法,自动获取当前线程中的ThreadLocalMap对象,并获取相应的变量值。
ThreadLocal并不是什么线程,确切地说是一个线程的变量,通过该变量,可以实现多线程之间的安全访问全局变量。ThreadLocal同synchronized,都是解决线程安全问题,ThreadLocal是通过“空间换取时间”的方式,synchronized是通过“时间换取空间”的方式。