共计 1955 个字符,预计需要花费 5 分钟才能阅读完成。
1. ThreadLocal 作用
ThreadLocal 的作用是使得每个线程 都能领有各自独立的对象正本 ,假如多个线程领有同一个实例,ThreadLocal<T>
类型的变量在每个线程中都有一个正本,从而为变量提供了 线程间隔离 的作用。
2. ThreadLocal 实例
public class ThreadLocalDemo implements Runnable{private static ThreadLocal<People> local = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {ThreadLocalDemo localDemo = new ThreadLocalDemo();
Thread thread1 = new Thread(localDemo);
thread1.start();
Thread.sleep(1000);
Thread thread2 = new Thread(localDemo);
thread2.start();}
@Override
public void run() {People people = local.get();
if (people==null) {people = new People();
people.setName("djh");
people.setAge(new Random().nextInt());
local.set(people);
}
System.out.println(people);
}
}
public class People {
String name;
int age;
public void setName(String name) {this.name = name;}
public void setAge(int age) {this.age = age;}
}
在该例子中,thread1
和 thread2
领有同一个对象 localDemo
,如果不应用ThreadLocal
包装,则两个线程拜访的 people 应该是同一个。而在咱们应用 ThreadLocal
包装后,运行后果如下:
bean.People@6fb35e7d
bean.People@614b201a
两者拜访的 people 是不一样的两个对象,而这就是 ThreadLocal 的作用,提供了 变量线程间的隔离能力。
3. 源码解析
查看 ThreadLocal 的 set 办法,
public void set(T value) {Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
- 先获取以后线程;
- 获取以后线程的 threadLocals 变量(是一个
ThreadLocalMap
类型的 Map); - 如果 map 不为空,为这个 map 插入 < 以后 ThreadLocal 对象,值 > 的键值对;
- 如果该 map 为空,则创立一个一个 ThreadLocalMap 对象,
t.threadLocals = new ThreadLocalMap(this, firstValue)
因为每个线程都有各自的 threadLocals,所以多个线程运行时 通过第二步拿到的 map 是不一样的,这样就实现了线程间隔离。
4. ThreadLocal 的内存透露问题
由第 3 节能够得悉,数据理论存在每个线程的 threadLocals 成员(即一个ThreadLocalMap 对象)里,ThreadLocalMap 和一般的 map 一样应用 Entry 数组存储数据,这个 Entry 非凡的中央在于,它继承了弱援用,即该 Entry 的 key 是一个 ThreadLocal 对象的弱援用。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {super(k);
value = v;
}
}
弱援用的特点是,如果某个对象只被弱援用关联,在下一次 GC 时该对象就会被回收。此时某个 ThreadLocal
类型变量应用完被咱们置为 null,此时 Entry 中关联的 Key 会变成 null,但 Value 因为是强援用,且运行的线程指向他,因而 Value 局部产生了内存透露。
线程执行结束被回收时 Value 会随之回收,但当咱们应用线程池时,因为线程会复用,因而 Value 会始终存在,这就产生了更为严重的内存透露。
5. 参考资料
- 正确理解 Thread Local 的原理与实用场景
- ThreadLocal 内存透露问题深入分析