参考:
https://www.cnblogs.com/ablejava/p/5914090.html 根本原因分析
1,常规使用
static final ThreadLocal<Long> longLocal = new ThreadLocal<>();
static final ThreadLocal<String> stringLocal = new ThreadLocal<>();
// 被调用的 入口
public void testThreadLocal(String threadNameA, String threadNameB) {
set();
new Thread(new Runnable() {
@Override
public void run() {
set();
get();
}
}, threadNameA).start();
new Thread(new Runnable() {
@Override
public void run() {
set();
get();
}
}, threadNameB).start();
get();
}
// 统一,放入数据
private void set() {
longLocal.set(Thread.currentThread().getId());
stringLocal.set(Thread.currentThread().getName());
}
// 统一,获取数据,并打印
private void get() {
long longNum = longLocal.get();
String strName = stringLocal.get();
LogUtil.v("longNum = " + longNum + ", strName = " + strName);
}
打印结果:
V/xxx->MainActivity.get(L:57):: LogUtil -> longNum = 56015, strName = newThreadA
V/xxx->MainActivity.get(L:57):: LogUtil -> longNum = 56016, strName = newThreadB
V/xxx->MainActivity.get(L:57):: LogUtil -> longNum = 1, strName = main
总结:
* 以上测试代码也是系统源码中使用ThreadLocal的方式
* 以上测试结果,仅仅表明:ThreadLocal能够避免线程安全问题,因为多线程操作并没有产生冲突(多次尝试打印结果一样)
2,ThreadLocal的set、get方法
1) set方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); // 获取旧的ThreadLocalMap
if (map != null)
map.set(this, value); // 直接放置数据
else
createMap(t, value); // 创建新的ThreadLocalMap
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
set的作用为:若当前线程,无ThreadLocalMap,则创建并放置数据;若当前线程,有ThreadLocalMap,则继续放入数据
2) 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();
}
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;
}
2) get方法
get的作用为:若当前线程,有ThreadLocalMap,则获取其中的数据;若当前线程,无ThreadLocalMap,则获取默认初始化的数据
总结:
* ThreadLocal放置和获取数据,都是以 this和value作为一对放入;因此,一个threadLocal,仅仅对应一个value
* ThreadLocal<T> 中的泛型,和value的数据类型一致
* ThreadLocal每次存放数据,都是放置到ThreadLocalMap中,而ThreadLocalMap是属于Thread的私有变量
3,ThreadLocalMap
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private static final int INITIAL_CAPACITY = 16; // 初始容量
private Entry[] table; // 具体存储的数据
}
总结:
* ThreadLocalMap和普通的map相同,是以数组作为实际的存储对象
* ThreadLocalMap中数组对应的是,Entry(ThreadLocal、Value),并且key和value是弱引用;即:一旦某一方被回收,另一方是不会导致内存泄漏的
4,整体内存分析
1) 内存是以Thread为主,Thread持有ThreadLocalMap、ThreadLocalMap存放着(ThreadLocal<Value> - Value)这样的数组
因此,当多线程环境下,对应的引用关系如下图所示:

结论:多线程环境下,ThreadLocal是静态类型,因此多线程的key是相同的,但value是不同的;换句话说:value就是Thread的私有成员变量
5,依据以上分析解决常见的疑问
1) ThreadLocal 线程安全的原理
线程安全的核心在于:多线程在使用共享数据时,造成的冲突;因此,对于私有变量而言,自然而然就没有线程安全的问题。
而value每次通过Thread获取到的都是当前线程的私有变量,并没有多线程共享该数据,当然没有线程安全的问题
2) ThreadLocal 是否存在内存泄漏
ThreadLocalMap的存储Entry{ThreadLocal - Value}关联方式为弱引用,因此当某个ThreadLocal置空,gc时,Value是能够被回收掉的;因此不存在内存泄漏问题
3) ThreadLocal 如何使用
* ThreadLocal一定是共享的,最好是static final类型,原因:ThreadLocal<T>,可以通过T来固定Value为单一类型;并且static final类型,能够确保在内存中为一份且不可改变
* Value值一定不能是static类型,若Value为static类型,ThreadLocal线程安全将失效
4) ThreadLocal 使用空间换时间,如何理解
* 通过增加数组(存放引用)以及每个线程备份Value的方式,解决线程安全的问题
demo地址:
2) get方法