java.lang包下的一个类,设计的目的是为了解决并发时,线程变量共享的问题,对于线程变量共享这个部分知识基本上是盲区,实际项目中也会用到这个ThreadLocal对象,我们项目中保存像用户信息这种全局变量就是通过ThreadLocal来实现的
注意不能将其翻译为线程本地化或本地线程,英语恰当的名称应该叫作:CopyValueIntoEveryThread
ThreadLocal中的两个静态内部类
- ThreadLocalMap,内部使用Entry做为存储数据的结构
- Entry:是ThreadLocalMap类中的静态内部类,继承了WeakReference(弱引用)
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
引用类型
强引用,软引用,弱引用,虚引用
ThreadLocal的价值
ThreadLocal的副作用
1 🔔🔔 脏数据
线程的复用会产生脏数据
2 🔥🔥 内存泄漏
分析:在源码注释中提示使用static关键字来修饰ThreadLocal。在此场景下当ThreadLocal对象失去引用后,触发弱引用机制来回收Entry的value就不现实了
解决方案:每次用完ThreadLocal时必须要及时调用remove()方法清理
注意事项
ThreadLocal对象通常作为静态私有变量来使用,那么其生命周期不会随着线程的结束而结束
线程使用ThreadLocal有3个重要方法
1 set() 如果没有set方法操作ThreadLocal,容易引起脏数据问题
2 get()方法,始终没有get操作的ThreadLocal对象是没有意义的
3 remove() 如果没有remove方法,容易引起内存泄露
项目中是怎么用的
先定义一个FilterContextHandler 对象,在对象中定义一个ThreadLocal对象,设置set和get方法以及remove方法
public class FilterContextHandler {
// ThreadLocal中放置map或者对象都可以
public static ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<Map<String, Object>>();
public static void set(String key, Object value) {
Map<String, Object> map = threadLocal.get();
if (map == null) {
map = new HashMap<String, Object>();
threadLocal.set(map);
}
map.put(key, value);
}
public static Object get(String key) {
Map<String, Object> map = threadLocal.get();
if (map == null) {
map = new HashMap<String, Object>();
threadLocal.set(map);
}
return map.get(key);
}
public static String getUserID() {
Object value = get(CommonConstants.CONTEXT_USER_ID);
return returnObjectValue(value);
}
public static String getOrgID() {
Object value = get(CommonConstants.CONTEXT_ORG_ID);
return returnObjectValue(value);
}
public static String getUsername() {
Object value = get(CommonConstants.CONTEXT_USERNAME);
return returnObjectValue(value);
}
public static String getName() {
Object value = get(CommonConstants.CONTEXT_NAME);
return returnObjectValue(value);
}
public static String getToken() {
Object value = get(CommonConstants.CONTEXT_TOKEN);
return returnObjectValue(value);
}
public static void setToken(String token) {
set(CommonConstants.CONTEXT_TOKEN, token);
}
public static void setName(String name) {
set(CommonConstants.CONTEXT_NAME, name);
}
public static void setUserID(String userID) {
set(CommonConstants.CONTEXT_USER_ID, userID);
}
public static void setOrgID(String orgID) {
set(CommonConstants.CONTEXT_ORG_ID, orgID);
}
public static void setUsername(String username) {
set(CommonConstants.CONTEXT_USERNAME, username);
}
private static String returnObjectValue(Object value) {
return value == null ? null : value.toString();
}
public static void remove() {
threadLocal.remove();
}
}
2 在拦截器的prehandle()中解析token然后将得到的用户信息存储到FilterContextHandler对象中
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token =
request.getHeader(CommonConstants.CONTEXT_TOKEN);
if(!StringUtils.isNotBlank(token)){
token = request.getParameter("Authorization");
}
if(StringUtils.isNotBlank(token)) {
UserToken userToken =
JwtUtils.getInfoFromToken(token);
FilterContextHandler.setToken(token);
String username = userToken.getUsername();
String name = userToken.getName();
String userId = userToken.getUserId();
String orgId = userToken.getOrgId();
UserToken userTokennew = new UserToken(username, userId, username, orgId);
token = JwtUtils.generateToken(userToken, 2*60*60*1000);
logger.info("------设置token" + Thread.currentThread().getId());
FilterContextHandler.setUsername(userToken.getUsername());
FilterContextHandler.setName(userToken.getName());
FilterContextHandler.setUserID(userToken.getUserId());
FilterContextHandler.setOrgID(userToken.getOrgId());
}
return super.preHandle(request, response, handler);
}
3 项目中用到的需要获取用户信息时只需要调用对应的get方法即可
FilterContextHandler.getUserID()
spring中的应用
Spring框架里面就是用的ThreadLocal来实现这种隔离,主要是在TransactionSynchronizationManager这个类里面,代码如下所示:
private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal("Transaction synchronizations");
private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal("Current transaction name");
private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal("Current transaction read-only status");
private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal("Current transaction isolation level");
private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal("Actual transaction active");
Spring的事务主要是ThreadLocal和AOP去做实现的
FilterChain的子类中也用到了ThreadLocal
未完待续......
评论区