java坑之ThreadLocal缓存使用需及时失效
【摘要】 当我们使用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)