java坑之ThreadLocal缓存使用需及时失效

举报
object 发表于 2024/02/20 20:13:46 2024/02/20
【摘要】 当我们使用TheadLocal来作为缓存时,可能会导致缓存异常的情况,参考如下代码:每次访问,打印线程名字,并获取当前线程的TheadLocal中的缓存,然后设置缓存,再次打印缓存值。我们自认为的情况是before会永远为null,实际并不是private static final ThreadLocal<String> CURRENTUSER = ThreadLocal.withIniti...

当我们使用TheadLocal来作为缓存时,可能会导致缓存异常的情况,参考如下代码:

每次访问,打印线程名字,并获取当前线程的TheadLocal中的缓存,然后设置缓存,再次打印缓存值。我们自认为的情况是before会永远为null,实际并不是

private static final ThreadLocal<String> CURRENTUSER = ThreadLocal.withInitial(() -> null);

@RequestMapping(value = "/a/b/c", method = RequestMethod.GET)
public JsonResponse<String> redirect3(
        @RequestParam(name = "uid", required = false) String uid) {
    System.out.println("当前线程:" + Thread.currentThread().getName());
    String before = CURRENTUSER.get();
    System.out.println("设置前,获取当前用户:" + before);
    if (before != null) {
        System.out.println("============异常===========:" + Thread.currentThread().getName());
        return JsonResponse.fail("fail");
    }
    CURRENTUSER.set(uid);
    String after = CURRENTUSER.get();
    System.out.println("设置后,获取当前用户:" + after);
    return JsonResponse.success(null, "success");
}

多次访问该接口后,输出结果如下

// 第一次打印
当前线程:130
设置前,获取当前用户:null
设置后,获取当前用户:123

// 第二次打印
当前线程:121
设置前,获取当前用户:null
设置后,获取当前用户:123
// 第三次打印
当前线程:121
设置前,获取当前用户:123
============异常===========:121

原因是springmvc内置的tomcat存在线程池的概念,线程池就有核心值,最大值,就会存在有线程一直存活的状态,导致了上述的异常情况。一般线程值为10就会重复

// todo 验证了多次设置最大线程值server.tomcat.max-threads=1,但是失败了,暂未找到原因,后续排查。

正确的做法,需要使用完后,在线程执行结束前,手动的进行清理缓存。最好是finally中进行,防止异常情况未及时清理。再次验证,不再触发异常情况

private static final ThreadLocal<String> CURRENTUSER = ThreadLocal.withInitial(() -> null);

@RequestMapping(value = "/a/b/c", method = RequestMethod.GET)
public JsonResponse<String> redirect3(@RequestParam(name = "uid", required = false) String uid) {
        System.out.println("当前线程:" + Thread.currentThread().getId());
        String before = CURRENTUSER.get();
        System.out.println("设置前,获取当前用户:" + before);
        if (before != null) {
            System.out.println("============异常===========:" + Thread.currentThread().getId());
            return JsonResponse.fail("fail");
        }
        try {
            CURRENTUSER.set(uid);
            String after = CURRENTUSER.get();
            System.out.println("设置后,获取当前用户:" + after);
            return JsonResponse.success(null, "success");
        } finally {
            CURRENTUSER.remove();
        }
}
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。