前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring 单例 Bean 与多线程深度分析

Spring 单例 Bean 与多线程深度分析

作者头像
一个会写诗的程序员
发布2022-01-07 17:55:13
2.5K0
发布2022-01-07 17:55:13
举报
文章被收录于专栏:一个会写诗的程序员的博客

Spring 的 bean默认是单例的,在高并发下,如果在 Spring 的单例 bean 中设置成员变量,则会发生并发问题。最近在进行开发时,错误的在单例的bean中使用了成员变量,导致多个线程大并发访问时,出现赋值错误及日志打印混乱的问题。

本文就对单例 bean 及多线程安全的问题做一次较为深入的探讨,也是对自我的一次反省,之后的开发中,杜绝此类问题,修正开发习惯。

单例模式

首先我们回顾一下单例模式的概念。单例模式的意思是只有一个实例,例如在Spring容器中某一个类只有一个实例,而且自行实例化后并项整个系统提供这个实例,这个类称为单例类。

当多个用户同时请求一个服务时,容器会给每一个请求分配一个线程,这时多个线程会并发执行该请求对应的业务逻辑(成员方法),此时就要注意了,如果该处理逻辑中有对单例状态的修改(体现为该单例的成员属性),则必须考虑线程同步问题。

一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如 RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder 等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。

那么如何提升bean的线程安全呢?

简单的说,若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;

若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。

更进一步划分:

常量始终是线程安全的,因为只存在读操作。 每次调用方法前都新建一个实例是线程安全的,因为没有访问共享的资源。 局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量和方法内变量。这也就是我们常说的方法封闭。 如果实例无状态,则是线程安全的。如果实例中存在对同一个值的不同的操作行为,或者值在不同线程中都会变,那么就需要注意,不要使用成员变量存储属性。

这里我们引入无状态bean和有状态bean。

有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象,可以保存数据,是非线程安全的。在不同方法调用间不保留任何状态。

无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象 .不能保存数据,是不变类,是线程安全的。

在spring中无状态的Bean适合用不变模式,就是单例模式,这样可以共享实例提高性能。

有状态的Bean在多线程环境下不安全,适合用 Prototype 原型模式。

Prototype: 每次对 bean 的请求都会创建一个新的 bean 实例。

Servlet是单例多线程

struts2每次处理一个请求,struts2就会实例化一个对象,这样就不会有线程安全的问题了。Struts2 是线程安全的,当然前提情况是,Action 不交给 spring管理,并且不设置为单例。

Spring mvc 线程不安全的原因

请求时多线程请求的,但是每次请求过来调用的Controller对象都是一个,而不是一个请求过来就创建一个controller对象

原因就在于如果这个controller对象是单例的,那么如果不小心在类中定义了类变量,那么这个类变量是被所有请求共享的,

这可能会造成多个请求修改该变量的值,出现与预期结果不符合的异常

在单例的情况下 相当于所有类变量对于每次请求都是共享的,每一次请求对类变量的修改都是有效的

那有没有办法让controller不以单例而以每次请求都重新创建的形式存在呢?答案是当然可以,只需要在类上添加注解@Scope("prototype")即可,这样每次请求调用的类都是重新生成的(每次生成会影响效率)还有其他方法么?

答案是肯定的!使用ThreadLocal 来保存类变量,将类变量保存在线程的变量域中,让不同的请求隔离开来.

注:servlet Struts1 SpringMvc 是线程不安全的,当然如果你不使用实例变量也就不存在线程安全的问题了。

总结

为避免发生线程安全问题,在开发和设计系统的时候注意下一下三点:

自己写公用类的时候,要对多线程调用情况下的后果在注释里进行明确说明 对线程环境下,对每一个共享的可变变量都要注意其线程安全性 我们的类和方法在做设计的时候,要尽量设计成无状态的

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021.07.20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 单例模式
  • 那么如何提升bean的线程安全呢?
  • Servlet是单例多线程
  • Spring mvc 线程不安全的原因
  • 总结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档