简介
ThreadLocal是JDK提供的,支持线程本地变量。也就是说,如果我们创建了一个ThreadLocal变量,则访问这个变量的每个线程都会有这个变量的一个本地副本。如果多个线程同时对这个变量进行读写操作时,实际上操作的是线程自己本地内存中的变量,从而避免了线程安全的问题。
示例
public class ThreadLocalTest {
private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
public static void main(String[] args) {
//创建第一个线程
Thread threadA = new Thread(() -> {
threadLocal.set("ThreadA:" + Thread.currentThread().getName());
System.out.println("线程A本地变量中的值为:" + threadLocal.get());
});
//创建第二个线程
Thread threadB = new Thread(() -> {
threadLocal.set("ThreadB:" + Thread.currentThread().getName());
System.out.println("线程B本地变量中的值为:" + threadLocal.get());
});
//启动线程A和线程B
threadA.start();
threadB.start();
}
}
源码
get()
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程的threadLocals
ThreadLocalMap map = getMap(t);
if (map != null) {
//如果threadLocals 不为null,则获取当前线程存储的数据
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {//存在 则返回
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//不存在 则初始化threadLocals
return setInitialValue();
}
getMap()
ThreadLocalMap getMap(Thread t) {
//返回线程中的threadLocals成员变量
return t.threadLocals;
}
public class Thread implements Runnable {
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
setInitialValue()
private T setInitialValue() {
//初始化值 为null
T value = initialValue();
//获取当前线程
Thread t = Thread.currentThread();
//获取当前的ThreadLocal
ThreadLocalMap map = getMap(t);
if (map != null)//如果ThreadLocal不为null 则将value 设置到本地线程中
map.set(this, value);
else//否则创建 ThreadLocal
createMap(t, value);
}
createMap()
void createMap(Thread t, T firstValue) {
//创建ThreadLocalMap 并且将value添加到其中
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
set()
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程中的threadLocals本地线程变量
ThreadLocalMap map = getMap(t);
if (map != null)//如果不为null
map.set(this, value);//则将value存储到本地线程中
else
//否则创建threadLocals 并且将value存储到本地线程中
createMap(t, value);
}
remove()
public void remove() {
//根据当前线程获取当前线程的本地线程变量
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)//如果不为空 则删除,这里提醒一下,不管有没有异常,最后使用完成之后,手动调用一下remove方法,防止内存溢出
m.remove(this);
}
ThreadLocalMap
简介
ThreadLocalMap本身不是Map,但是可以实现以key-value的形式存储线程的局部变量
构造函数
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
// 创建一个容量为16的Entry数组
table = new Entry[INITIAL_CAPACITY];
// 使用散列算法计算第一个键值对在数组中的索引
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
// 创建Entry对象,并存放在Entry数组的索引对应位置
table[i] = new Entry(firstKey, firstValue);
size = 1;
// 根据Entry数组初始容量大小设置扩容阈值
setThreshold(INITIAL_CAPACITY);
}
ThreadLocalMap的散列算法为将ThreadLocal的哈希码与Entry数组长度减一做相与操作,由于Entry数组长度为2的幂次方,因此上述散列算法实质是ThreadLocal的哈希码对Entry数组长度取模。通过散列算法计算得到初始键值对在Entry数组中的位置后,会创建一个Entry对象并存放在数组的对应位置。最后根据公式:len * 2 / 3计算扩容阈值。
set()
// 调用set()方法时会传入一对键值对
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
// 通过散列算法计算键值对的索引位置
int i = key.threadLocalHashCode & (len-1);
// 遍历Entry数组
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
// 获取当前Entry的键
ThreadLocal<?> k = e.get();
// 当前Entry的键与键值对的键相等(即指向同一个ThreadLocal对象),则更新当前Entry的value为键值对的值
if (k == key) {
e.value = value;
return;
}
// 当前Entry的键被垃圾回收了,这样的Entry称为陈旧项,则根据键值对创建Entry并替换陈旧项
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
// 此时i表示遍历Entry数组时遇到的第一个空槽的索引
// 程序运行到这里,说明遍历Entry数组时,在遇到第一个空槽前,遍历过的Entry的键与键值对的键均不相等,同时也没有陈旧项
// 此时根据键值对创建Entry对象并存放在索引为i的位置(即空槽的位置)
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
// Entry数组中键值对数量大于等于阈值,则触发rehash()
// rehash()会先遍历Entry数组并删除陈旧项,如果删除陈旧项之后,键值对数量还大于等于阈值的3/4,则进行扩容
// 扩容后,Entry数组长度应该为扩容前的两倍
rehash();
}
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
getEntry()
private Entry getEntry(ThreadLocal<?> key) {
// 使用散列算法计算索引
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
// 如果Entry数组索引位置的Entry的键与key相等,则返回这个Entry
return e;
else
// 没有找到key对应的Entry时会执行getEntryAfterMiss()方法
return getEntryAfterMiss(key, i, e);
}
// 该方法一边遍历Entry数组寻找键与key相等的Entry,一边清除陈旧项
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
Entry
//这里的ThreadLocal是弱引用,即key是弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry是一个弱引用对象,key引用的ThreadLocal为被弱引用的对象,value引用的对象(上图中的Object)为被强引用的对象,那么在这种情况下,key引用的ThreadLocal不存在其它引用后,在下一次垃圾回收时key引用的ThreadLocal会被回收,防止了ThreadLocal对象的内存泄漏。key引用的ThreadLocal被回收后,此时这个Entry就成为了一个陈旧项,如果不对陈旧项做清除,那么陈旧项的value引用的对象就永远不会被回收,也会产生内存泄漏,所以ThreadLocal采用了线性探测来清除陈旧项,从而防止了内存泄漏。
ThreadLocal变量不具有传递性
使用ThreadLocal存储本地变量不具有传递性,也就是说,同一个ThreadLocal在父线程中设置值后,在子线程中是无法获取到这个值的,这个现象说明ThreadLocal中存储的本地变量不具有传递性。
public class ThreadLocalTest {
private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
public static void main(String[] args) {
//在主线程中设置值
threadLocal.set("ThreadLocalTest");
//在子线程中获取值
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程获取值:" + threadLocal.get());
}
});
//启动子线程
thread.start();
//在主线程中获取值
System.out.println("主线程获取值:" + threadLocal.get());
}
}
InheritableThreadLocal
示例
public class ThreadLocalTest {
private static ThreadLocal<String> threadLocal = new InheritableThreadLocal<String>();
public static void main(String[] args) {
//在主线程中设置值
threadLocal.set("ThreadLocalTest");
//在子线程中获取值
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程获取值:" + threadLocal.get());
}
});
//启动子线程
thread.start();
//在主线程中获取值
System.out.println("主线程获取值:" + threadLocal.get());
}
}
源码分析
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
protected T childValue(T parentValue) {
return parentValue;
}
//这里返回的是Thread中的inheritableThreadLocals
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
//这里返回的Thread中的inheritableThreadLocals
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
初始化InheritableThreadLocal
public class Thread implements Runnable {
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
//省略...
Thread parent = currentThread();
//省略......
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
//省略......
}
}
public class ThreadLocal<T> {
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
//这里就是将父Thread的inheritableThreadLocals中的数据复制到子Thread的inheritableThreadLocals中
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
}