首先写个简单的列子:
public void threadLocalTest(){
//new一个ThreadLocal对象
ThreadLocal td = new ThreadLocal();
td.set("缓存的数据");
}
当ThreadLocal 执行set()方法时,通过查看源码可知,首先得到了当前线程,并通过getMap()方法得到当前线程的成员变量 ThreadLocalMap,并用ThreadLocal(this)做key,将set进的value放入当前线程的ThreadLocalMap中。将值缓存在线程中。
进入set方法中:
进入Entry类中:
发现Entry继承了弱引用,所以此时作为key的ThreadLocal在这里是弱引用,之所以用弱引用的原因是:当栈中对new ThreadLocal()的强引用td消失时,new ThreadLocal() 应该被GC,而如果此时作为key的ThreadLocal不是弱引用而是强引用,则会导致堆中的new ThreadLocal()不能被回收。很容易发生内存泄漏。
那么,当多线程的情况下,如果希望子线程也同样能够使用当前线程中绑定的数据该如何实现呢?
在此背景下,产生了InheritableThreadLocal,InheritableThreadLocal继承了ThreadLocal,会将主线程中ThreadLocalMap中的所有数据复制到自己的ThreadLocalMap中。
代码应用实例:
编写工具类:
package com.qihoo.finance.lbgw.ctrl.threadLocalMap;
import org.apache.commons.lang3.StringUtils;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/***
* ThreadLocal 的应用之:InheritableThreadLocal继承了ThreadLocal:子线程会将主线程中ThreadLocalMap中的所有数据复制到自己的Map中
*/
public class ServiceContext {
private static final ThreadLocal<ServiceContext> context = new InheritableThreadLocal<ServiceContext>() {
//只有子线程执行的时候,才执行这个函数
@Override
protected ServiceContext childValue(ServiceContext parentValue) {
System.out.println("将主线程中的数据复制到子线程");
ServiceContext context = new ServiceContext();
if (parentValue != null) {
context.initBy(parentValue);
}
return context;
}
@Override
protected ServiceContext initialValue() {
System.out.println("初始化一个value,放入线程,相当于 context.set(new ServiceContext())");
return new ServiceContext();
}
};
/**
* 将主线程中的数据复制到子线程
* @param serviceContext
*/
public void initBy(ServiceContext serviceContext) {
if (serviceContext == null || serviceContext == this) {
return;
}
contextVar.clear();
contextVar.putAll(serviceContext.getContextVar());
}
private Map<String, String> contextVar = new ConcurrentHashMap<>();
public static ServiceContext getContext() {
System.out.println("获取线程中的成员变量ThreadLocalMap 中的ServiceContext");
return context.get();
}
public static ServiceContext getContext(String prefix) {
if (StringUtils.isBlank(prefix)) {
return context.get();
}
return context.get();
}
public void clearContextVar() {
contextVar.clear();
}
public void removeContextVar(String key) {
if (contextVar.containsKey(key)) {
contextVar.remove(key);
}
}
public Map<String, String> getContextVar() {
return contextVar;
}
public String getContextVar(String key) {
return contextVar.get(key);
}
public void addContextVar(String key, String value) {
if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
return;
}
contextVar.put(key, value);
}
public String getTenantCode() {
return this.getContextVar("tenantCode");
}
public void setTenantCode(String tenantCode) {
this.addContextVar("tenantCode", tenantCode);
}
}
测试看执行过程:
public void test(){
//主线程绑定数据
ServiceContext.getContext().setTenantCode("a");
new Thread(new Runnable() {
@Override
public void run() {
//查看子线程中是否有主线程中缓存的数据
System.out.println("子线程中缓存数据:"+ServiceContext.getContext().getTenantCode());
}
}).start();
System.out.println(ServiceContext.getContext().getTenantCode());
}
PS:除了观察子线程中是否成功继承了主线程中的数据外,注意工具类中的方法执行顺序,理解InheritableThreadLocal实现数据copy的过程。