- 作用:为每个线程创建一个变量副本,只能该线程自己才能访问。
验证实现:
public class Test {
ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
//一个任务,两个线程t1、t2
MyTask task = new Test().new MyTask();
new Thread(task,"t1").start();
Thread.sleep(10); //中间睡眠10ms,放大效果
new Thread(task,"t2").start();
}
class MyTask implements Runnable{
@Override
public void run() {
Long aLong = threadLocal.get();
if(aLong == null){
threadLocal.set(System.currentTimeMillis());
}
System.out.println(Thread.currentThread().getName() + "=>" + threadLocal.get());
}
}
}
//结果:
//t1=>1601812794882
//t2=>1601812794892
//相差10ms
结果不同,说明aLong变量为null,t1和t2线程使用threadLocal.get() 获取的内容不相同,是因为每个线程都有一个map存储该线程自己的东西。
源码分析:
// ThreadLocal类
//ThreadLocal的get()方法:
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//通过线程获取一个map,不同线程所操作的map是不同的。
ThreadLocalMap map = getMap(t);
if (map != null) {
//以this为key,即threadLocal对象为key,获取entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
//entry不为null,则获取entry中存放的变量的副本value
T result = (T)e.value;
return result;
}
}
//没有则返回null
return setInitialValue();
}
//ThreadLocal的set()方法:
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//通过线程获取map,由于是统一线程,所以获取的map是同一个。
ThreadLocalMap map = getMap(t);
if (map != null)
//以this,即threadLocal对象作为key,value:保存的副本,添加到当前线程的map中去。
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
//返回本地线程的一个ThreadLocalMap成员变量
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
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;
}
protected T initialValue() {
return null;
}
// Thread类的静态内部类ThreadLocalMap
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//....
}
ThreadLocal的使用场景:
当service层调用的多个dao层的方法时,需要对多个dao层的方法实现事务的控制,让其中一个dao的方法执行失败时,其他的dao方法会回滚,由于是在同一个线程,所以使用ThreadLocal保存connection对象,来达到所有dao层方法使用的是同一个connection对象,这样可以保证全部dao执行都commit或者rollback。
ThreadLocal内存泄露的原因:
在线程池中使用ThreadLocal时,可能存在内存泄露。由于使用ThreadLocal的引用链是:Thread->ThreadLocalMap->Entry->(ThreadLocal,value),当我们在某个线程中使用完ThreadLocal之后,本应该ThreadLocal会随着线程的销毁而销毁,但是线程池创建的线程可能不会销毁,会进行线程的复用,导致Entry被强引用着,不会被垃圾回收,但是ThreadLocal对象在Entry中是弱引用
,会被GC回收,就会导致Enrty(null,value),我们的value无法被使用到,只要线程不销毁,就会一直存在着,从导致value内存泄露。
ThreadLocal的key设置为弱引用的原因:
防止key的内存泄露。如果key为强引用,使用threadLocal时,Entry的key和外部变量t引用着threadLocal对象,此时如果如果t断开了引用,但是Entry的key还强引用着这个threadLocal对象,只要线程不销毁,那么这个threadLocal对象就一直存在着,造成内存泄漏。如果key为弱引用,那么下次gc时就会回收掉。