当前位置: 首页 > news >正文

java 网站开发总结如何找做网站的公司

java 网站开发总结,如何找做网站的公司,个人邮箱申请,seo网站推广怎么做在生产上遇到一个诡异的问题,有时获取到的用户信息是别人的。查看代码后,我发现他使用了 ThreadLocal 来缓存获取到的用户信息。 我们知道,ThreadLocal 适用于变量在线程间隔离,而在方法或类间共享的场景。如果用户信息的获取比较…

在生产上遇到一个诡异的问题,有时获取到的用户信息是别人的。查看代码后,我发现他使用了 ThreadLocal 来缓存获取到的用户信息。

我们知道,ThreadLocal 适用于变量在线程间隔离,而在方法或类间共享的场景。如果用户信息的获取比较昂贵(比如从数据库查询用户信息),那么在 ThreadLocal 中缓存数据是比较合适的做法。但,这么做为什么会出现用户信息错乱的 Bug 呢?

我们看一个具体的案例吧。

使用 Spring Boot 创建一个 Web 应用程序,使用 ThreadLocal 存放一个 Integer 的值,来暂且代表需要在线程中保存的用户信息,这个值初始是 null。在业务逻辑中,我先从 ThreadLocal 获取一次值,然后把外部传入的参数设置到 ThreadLocal 中,来模拟从当前上下文获取到用户信息的逻辑,随后再获取一次值,最后输出两次获得的值和线程名称。

private static final ThreadLocal<Integer> currentUser = ThreadLocal.withInitial(() -> null);@GetMapping("wrong")
public Map wrong(@RequestParam("userId") Integer userId) {//设置用户信息之前先查询一次ThreadLocal中的用户信息String before  = Thread.currentThread().getName() + ":" + currentUser.get();//设置用户信息到ThreadLocalcurrentUser.set(userId);//设置用户信息之后再查询一次ThreadLocal中的用户信息String after  = Thread.currentThread().getName() + ":" + currentUser.get();//汇总输出两次查询结果Map result = new HashMap();result.put("before", before);result.put("after", after);return result;
}

按理说,在设置用户信息之前第一次获取的值始终应该是 null,但我们要意识到,程序运行在 Tomcat 中,执行程序的线程是 Tomcat 的工作线程,而 Tomcat 的工作线程是基于线程池的。

顾名思义,线程池会重用固定的几个线程,一旦线程重用,那么很可能首次从 ThreadLocal 获取的值是之前其他用户的请求遗留的值。这时,ThreadLocal 中的用户信息就是其他用户的信息。

为了更快地重现这个问题,我在配置文件中设置一下 Tomcat 的参数,把工作线程池最大线程数设置为 1,这样始终是同一个线程在处理请求:

server.tomcat.max-threads=1

运行程序后先让用户 1 来请求接口,可以看到第一和第二次获取到用户 ID 分别是 null 和 1,符合预期:

 随后用户 2 来请求接口,这次就出现了 Bug,第一和第二次获取到用户 ID 分别是 1 和 2,显然第一次获取到了用户 1 的信息,原因就是 Tomcat 的线程池重用了线程。从图中可以看到,两次请求的线程都是同一个线程:http-nio-8080-exec-1。

这个例子告诉我们,在写业务代码时,首先要理解代码会跑在什么线程上:

  • 我们可能会抱怨学多线程没用,因为代码里没有开启使用多线程。但其实,可能只是我们没有意识到,在 Tomcat 这种 Web 服务器下跑的业务代码,本来就运行在一个多线程环境(否则接口也不可能支持这么高的并发),并不能认为没有显式开启多线程就不会有线程安全问题。 
  • 因为线程的创建比较昂贵,所以 Web 服务器往往会使用线程池来处理请求,这就意味着线程会被重用。这时,使用类似 ThreadLocal 工具来存放一些数据时,需要特别注意在代码运行完后,显式地去清空设置的数据。如果在代码中使用了自定义的线程池,也同样会遇到这个问题。

理解了这个知识点后,我们修正这段代码的方案是,在代码的 finally 代码块中,显式清除 ThreadLocal 中的数据。这样一来,新的请求过来即使使用了之前的线程也不会获取到错误的用户信息了。修正后的代码如下:

@GetMapping("right")
public Map right(@RequestParam("userId") Integer userId) {String before  = Thread.currentThread().getName() + ":" + currentUser.get();currentUser.set(userId);try {String after = Thread.currentThread().getName() + ":" + currentUser.get();Map result = new HashMap();result.put("before", before);result.put("after", after);return result;} finally {//在finally代码块中删除ThreadLocal中的数据,确保数据不串currentUser.remove();}
}

重新运行程序可以验证,再也不会出现第一次查询用户信息查询到之前用户请求的 Bug:

总结:  使用 ThreadLocal 来缓存数据,以为 ThreadLocal 在线程之间做了隔离不会有线程安全问题,没想到线程重用导致数据串了。请务必记得,在业务逻辑结束之前清理 ThreadLocal 中的数据。

http://www.yidumall.com/news/11770.html

相关文章:

  • 网站开发环境包括什么长尾关键词排名系统
  • 济南网站建设和优化代发qq群发广告推广
  • 徐州建设工程材料检测预约网站天津seo推广
  • 哪里有营销型网站微信推广软件有哪些
  • 桓台县建设局网站深圳整站seo
  • wordpress 网站很卡上海关键词排名手机优化软件
  • 网站导航网址大全站长之家seo一点询
  • 简单好看个人主页网站模板惠州网站营销推广
  • 专业制作网站 郑下载百度app到桌面
  • 云南网站建设天度品牌seo主要做什么
  • 营销型企业网站类型洛阳搜索引擎优化
  • 化妆品网站推广策划书营销网站建设的因素
  • 广西新宇建设项目有限公司网站快速网站推广
  • 用 net做网站sem竞价培训班
  • 网页设计风格seo网站优化网站编辑招聘
  • 做网站商机seo建设
  • 做网站的时候用的什么框架电商网站大全
  • 陕西省建设厅官网证件查询优化大师下载电脑版
  • 做视频网站要多大的服务器百度贴吧官网首页
  • 聊城做网站的公司咨询网络营销公司业务范围
  • 我想在购物网站做代理运用搜索引擎营销的案例
  • 个人网站,可以做淘宝客吗南和网站seo
  • 可以做蛋白三位结构图的网站seo 知乎
  • 做的网站如何更换网站模板如何做seo整站优化
  • 四川学校网站建设网络营销实训个人总结
  • 哪些网站专做自媒体的怎么做一个网站
  • 邢台做网站流程今日新闻 最新消息 大事
  • 网页设计师网站大全安卓优化大师app下载安装
  • 东莞市建设网站首页网络优化工程师主要做什么
  • 网站建设政府板块手机版谷歌浏览器入口