利用单个ThreadLocal对象,实现单线程的数据共享

本文介绍了在Spring MVC中如何利用ThreadLocal实现单线程间的数据共享,以解决已登录用户的user数据传递问题。通过拦截器获取cookie-token,从redis获取user数据并存入ThreadLocal中,最后在afterCompletion方法中释放ThreadLocal以防止内存泄漏。同时,文章解释了ThreadLocal的工作原理和在Spring中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Spring MVC 单条请求 之间共享数据
业务:已登录用户,访问controller,我们需要获得它的user数据;

解决思路

1.request 域

2.session域

--->需要在Controler 方法的参数上传入request,代码过于侵入式;

3.THreadLocal--->好

实现:1.拦截器中,获取cookie-token

    2.根据token,获取到redis中的user数据

    3.存在就放行,并将user数据写入threadLocal中;

   4.在拦截器的afterCompletion()方法里写上释放ThreadLocal对象的方法:因为GC无法回收ThreadLocal,会造成内存泄漏,从而长期以往造成内存溢出;



为了解决线程安全的问题,我们有3个思路:

第一每个线程独享自己的操作对象,也就是多例,多例势必会带来堆内存占用、频繁GC、对象初始化性能开销等待等一些列问题。

第二单例模式枷锁,典型的案例是HashTableVector,对读取和变更的操作用synchronized限制起来,保证同一时间只有一个线程可以操作该对象。虽然解决了内存、回收、构造、初始化等问题,但是势必会因为锁竞争带来高并发下性能的下降。

第三个思路就是今天重点推出的ThreadLocal。单例模式下通过某种机制维护成员变量不同线程的版本。


先来看Thread

Thread中维护了一个ThreadLocal.ThreadLocalMapthreadLocals = null; ThreadLocalMap这个MapkeyThreadLocalvalue是维护的成员变量。现在的跟踪链是Thread->ThreadLocalMap-><ThreadLocal,Object>,那么我们只要搞明白Thread怎么跟ThreadLocal关联的,从线程里找到自己关心的成员变量的快照这条线就通了。

  1. ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
  2. table = new Entry[INITIAL_CAPACITY];
  3. int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
  4. table[i] = new Entry(firstKey, firstValue);
  5. size = 1;
  6. setThreshold(INITIAL_CAPACITY);
  7. }

再来看ThreadLocal:它里面核心方法两个get()set(T)

  1. public T get() {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t);
  4. if (map != null) {
  5. ThreadLocalMap.Entry e = map.getEntry(this);
  6. if (e != null) {
  7. @SuppressWarnings("unchecked")
  8. T result = (T)e.value;
  9. return result;
  10. }
  11. }
  12. return setInitialValue();
  13. }

  1. public void set(T value) {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t);
  4. if (map != null)
  5. map.set(this, value);
  6. else
  7. createMap(t, value);
  8. }


方法里通过Thread.currentThread()的方法得到当前线程,然后做为key存储到当前线程对象的threadLocals中,也就是TreadLocalMap中。

OK,这样整个关系链已经建立,真正要去访问的成员变量在一个map中,key是线程号,值是属于该线程的快照。

ThreadLocal里还有map的创建createMap(t, value)、取值时对象的初始值setInitialValue()、线程结束时对象的释放remove()等细节,有兴趣的可以继续跟进了解下。

ThreadLocal应用其实很多,例如Spring容器中实例默认是单例的,transactionManager也一样,那么事务在处理时单例的manager是如何控制每个线程的事务要如何处理呢,这里面就应用了大量的ThreadLocal


工具类


/**
 * ThreadLocal 工具类
 * 用来在单线程之间共享数据
 * 用完记得关哦,不然会内存泄漏
 * @author luo
 *
 */
public class UserThreadLocal {
/**
* 1.定义ThreadLocal
* 使用规则:
* 1.如果有单个数据需要使用ThreadLocal进行数据的传输,则泛型就是单个对象的类型.
*  2.如果有多个数据需要使用ThreadLocal进行数据的传输,那么用Map
*/
private static ThreadLocal<User> threadLocal = new ThreadLocal<>();

public static User getUser(){
return threadLocal.get();
}

public static void setUser(User user){
threadLocal.set(user);
}
//切记用完要remove!!
public static void remove(){
threadLocal.remove();
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值