当前位置:   article > 正文

SpringBean默认是单例的,高并发情况下,如何保证并发安全?_单例bean的并发访问问题

单例bean的并发访问问题

public class HomeController {

private int i;

@GetMapping(“testsingleton1”)

@ResponseBody

public int test1() {

return ++i;

}

}

多次访问此url,可以看到每次的结果都是自增的,所以这样的代码显然是并发不安全的

二、解决方案


因此,我们为了让无状态的海量Http请求之间不受影响,我们可以采取以下几种措施:

2.1 单例变原型

对web项目,可以Controller类上加注解@Scope("prototype")@Scope("request"),对非web项目,在Component类上添加注解@Scope("prototype")

优点:实现简单;

缺点:很大程度上增大了bean创建实例化销毁的服务器资源开销。

2.2 线程隔离类ThreadLocal

有人想到了线程隔离类ThreadLocal,我们尝试将成员变量包装为ThreadLocal,以试图达到并发安全,同时打印出Http请求的线程名,修改代码如下:

@Controller

public class HomeController {

private ThreadLocal i = new ThreadLocal<>();

@GetMapping(“testsingleton1”)

@ResponseBody

public int test1() {

if (i.get() == null) {

i.set(0);

}

i.set(i.get().intValue() + 1);

log.info(“{} -> {}”, Thread.currentThread().getName(), i.get());

return i.get().intValue();

}

}

多次访问此url测试一把,打印日志如下:

[INFO ] 2019-12-03 11:49:08,226 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-1 -> 1

[INFO ] 2019-12-03 11:49:16,457 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-2 -> 1

[INFO ] 2019-12-03 11:49:17,858 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-3 -> 1

[INFO ] 2019-12-03 11:49:18,461 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-4 -> 1

[INFO ] 2019-12-03 11:49:18,974 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-5 -> 1

[INFO ] 2019-12-03 11:49:19,696 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-6 -> 1

[INFO ] 2019-12-03 11:49:22,138 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-7 -> 1

[INFO ] 2019-12-03 11:49:22,869 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-9 -> 1

[INFO ] 2019-12-03 11:49:23,617 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-8 -> 1

[INFO ] 2019-12-03 11:49:24,569 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-10 -> 1

[INFO ] 2019-12-03 11:49:25,218 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-1 -> 2

[INFO ] 2019-12-03 11:49:25,740 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-2 -> 2

[INFO ] 2019-12-03 11:49:43,308 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-3 -> 2

[INFO ] 2019-12-03 11:49:44,420 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-4 -> 2

[INFO ] 2019-12-03 11:49:45,271 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-5 -> 2

[INFO ] 2019-12-03 11:49:45,808 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-6 -> 2

[INFO ] 2019-12-03 11:49:46,272 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-7 -> 2

[INFO ] 2019-12-03 11:49:46,489 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-9 -> 2

[INFO ] 2019-12-03 11:49:46,660 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-8 -> 2

[INFO ] 2019-12-03 11:49:46,820 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-10 -> 2

[INFO ] 2019-12-03 11:49:46,990 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-1 -> 3

[INFO ] 2019-12-03 11:49:47,163 com.cjia.ds.controller.HomeController.test1(HomeController.java:50)

http-nio-8080-exec-2 -> 3

从日志分析出,二十多次的连续请求得到的结果有1有2有3等等,而我们期望不管我并发请求有多少,每次的结果都是1;同时可以发现web服务器默认的请求线程池大小为10,这10个核心线程可以被之后不同的Http请求复用,所以这也是为什么相同线程名的结果不会重复的原因。

总结:ThreadLocal的方式可以达到线程隔离,但还是无法达到并发安全。

2.3 尽量避免使用成员变量

有人说,单例bean的成员变量这么麻烦,能不用成员变量就尽量避免这么用,在业务允许的条件下,将成员变量替换为RequestMapping方法中的局部变量,多省事。这种方式自然是最恰当的,本人也是最推荐。代码修改如下:

@Controller

public class HomeController {

@GetMapping(“testsingleton1”)

@ResponseBody

public int test1() {

int i = 0;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

本次面试答案,以及收集到的大厂必问面试题分享:

字节跳动超高难度三面java程序员面经,大厂的面试都这么变态吗?

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
671a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />

本次面试答案,以及收集到的大厂必问面试题分享:

[外链图片转存中…(img-xq6BupV4-1713306009176)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/858494
推荐阅读
相关标签
  

闽ICP备14008679号