ThreadLocal的简单使用

ThreadLocal的简单使用

概述

1.ThreadLocal叫做“线程变量”,其中填充的变量属于当前线程,对其他线程是隔离的,不会被别的线程读取或修改。这种思路叫线程封闭。

原理是,每个线程内部都有一个ThreadLocalMap,它用ThreadLocal作为键,能够存储值。

    ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocal适合,每个线程需要有自己的实例,且该实例需要在多个方法间传递,但不希望线程间共享。

实例需要在多个方法间传递,就可以保存在当前线程的ThreadLocal中,就不需要通过参数传递了。需要时直接get()取出。

   public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

测试Demo

1.模拟用户登录后的业务操作,新建用户基本信息类

@Data
public class UserDto {

    private String userName;
    private String pwd;
    private String userId;
   // private byte[] info=new byte[1024*1024];
}

2.新建拦截器MyIntercepter

public class MyIntercepter implements HandlerInterceptor {
    /**
     * 处理请求前
     * @param request
     * @param response
     * @param handler
     * @return
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
        log.info("处理请求前");
        //模拟用户登录获取用户信息
        UserDto user = new UserDto();
        user.setUserName("张三");
        user.setPwd("1234");
        user.setUserId("222");
        UserLocalUtil.setUser(user);
        return true;
    }

    /**
     * 处理请求
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
   @Override
   public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                          @Nullable ModelAndView modelAndView) throws Exception {
       log.info("处理请求");
    }

    /**
     * 处理请求后
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
  public   void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                         @Nullable Exception ex) throws Exception {

        //UserLocalUtil.clear();
        log.info("处理请求后");
    }

3.添加拦截器配置

@Configuration
public class MyWebConfig implements WebMvcConfigurer {

    @Override
   public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(new MyIntercepter()).addPathPatterns("/**")
                .excludePathPatterns("**/user/login");
    }
}

4.新建测试controller

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
    
    @GetMapping("/getUser")
    public UserDto login(){
        log.info("开始执行方法");
        return UserLocalUtil.getUser();
    }
}

5.测试请求http://localhost:8001/user/getUser
在这里插入图片描述
使用多线程改变user对象的属性后进行测试

  @GetMapping("/getUser")
    public UserDto login(){
        log.info("开始执行方法");

        new Thread(new Runnable() {
            @Override
            public void run() {
                UserDto userDto = new UserDto();
                userDto.setUserName("测试");
                userDto.setPwd("111");
                UserLocalUtil.setUser(userDto);
            }
        }).start();

        return UserLocalUtil.getUser();
    }

继续请求后发现返回的数据依旧没有改变,证实了ThreadLocal的数据是线程内共享,线程间隔离。与之有类似的还有synchronized锁,该锁是线程间共享,保证该关键字修饰的代码块或者方法只能有一个线程进行访问。

注意事项

1.使用完ThreadLocal的数据后要进行清理,不然随着使用次数的增加,会导致系统内存溢出的风险。在这里插入图片描述
由于强引用的对象是不会被GC回收,所以存在内存溢出的风险,因此在使用完ThreadLocal后调用remove方法进行清除。
在这里插入图片描述
在这里插入图片描述
2.测试
设置启动参数,修改堆大小。-D -Xms20m -Xmx20m -Xmn20m。给user对象里增加字节对象,多次请求后控制台报错。
在这里插入图片描述
在这里插入图片描述

总结

1.应用场景
(1):用作保存每个线程独享的对象,为每个线程都创建一个副本,每个线程都只能修改自己所拥有的副本, 而不会影响其他线程的副本,在一定程度上让线程不安全的操作变得安全
(2):用作每个线程内需要独立保存信息的场景,供其他方法更方便得获取该信息,避免了方法之间的传参。
2.注意事项:使用完后调用remove方法进行清除,以免造成内存溢出。
有写的不对的地方欢迎指点,感谢。

  • 19
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值