如何使用
ThreadLocal 的使用比较简单,日常使用基本也只涉及存值和取值。
public class ThreadLocalTest {
public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
@Test
public void userThreadLocal(){
threadLocal.set("123");
String str = threadLocal.get();
System.out.println(str);
}
}
如何实现
整体设计思路如下:
- ThreadLocal中存在一个内部类ThreadLocalMap,用于存储需要存放在线程局部变量的数据,其key为当前ThreadLocal,其value为我们存入的值
- Thread类持有ThreadLocal.ThreadLocalMap变量
- 当前线程通过ThreadLocal的set方法在当前线程内部的ThreadLocal.ThreadLocalMap变量中以当前的threadLocal为key存入值;通过get方法,以当前的threadLocal为key从当前线程内部的ThreadLocalMap中取出存入的值
- 这样设计的效果就是每个线程存入值后都可以通过同一个ThreadLocal.get回各自存入的内容
关键方法
- set(T value)方法
//ThreadLcoal.set()
public void set(T value) {
Thread t = Thread.currentThread();//1.获得当前线程对象
ThreadLocalMap map = getMap(t);//2.获取当前线程内部的ThreadLocal.ThreadLocalMap对象
if (map != null)
map.set(this, value);//3.map不为空时以当前threadLocal为key存入value
else
createMap(t, value);//4.map为空时创建map
}
//2处的getMap方法,ThreadLcoal.getMap()
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;//返回当前线程内部的ThreadLocal.ThreadLocalMap对象
}
//4处的createMap方法,ThreadLcoal.createMap()
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
- get()方法
//ThreadLcoal.get()
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);//1.获取当前线程的ThreadLocal.ThreadLocalMap对象
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//2.当前线程的ThreadLocal.ThreadLocalMap对象不为空则以当前threadLocal为key取出Map内部的实体
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;//3.e.value即是我们存入的值
return result;
}
}
return setInitialValue();//4.当map为空时初始化map
}
//4处的setInitialValue方法,ThreadLcoal.setInitialValue()
private T setInitialValue() {
T value = initialValue();//初始值为null
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)//get中map为空才走的这个方法,且map仅属于当前线程,不存在线程安全问题,感觉没必要再次判空。欢迎指教
map.set(this, value);
else
createMap(t, value);
return value;
}
测试
public class ThreadLocalTest {
public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
@Test
public void testThreadLocal() throws InterruptedException {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1,5,1, TimeUnit.MINUTES,new LinkedBlockingDeque<>());
//第一个线程操作threadLocal
threadPoolExecutor.execute(()->{
threadLocal.set("first thread");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("firstThread.get: "+threadLocal.get());
});
//第二个线程操作threadLocal
threadPoolExecutor.execute(()->{
threadLocal.set("second thread");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("secondThread.get: "+threadLocal.get());
});
//主线程等待线程池内线程完成任务处理
threadPoolExecutor.awaitTermination(1,TimeUnit.MINUTES);
}
}
结果输出
补充:为什么ThreadLocalMap中Entry的key要使用软引用
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal> {
Object value;
Entry(ThreadLocal k, Object v) {//此处的key为软引用
super(k);
value = v;
}
}
//省略其他代码。。。
}
因为这个key是指向ThreadLocal的,请看如下代码:
public void testThreadLocal() {
ThreadLocal threadLocal = new ThreadLocal<String>();
threadLocal.set("abc");
threadLocal.get();
}
当此方法出栈时threadLocal本应被回收,但是若ThreadLocalMap.Entry的key为强引用的话threadLocal在当前线程消亡之前都不会被回收,存在内存泄漏的风险