Spring中单例Bean的线程安全问题采坑

 
    Spring集成的项目中,通过注解方式注入Bean的使用无处不在。在一次项目开发中,由voliate关键字思考到的Spring中单例Bean的线程安全问题,在此记录采坑总结。

    Spring的单例Bean(@Autowired等注解的Bean)默认是单例的,Spring并没有对Bean有线程安全的控制策略,并发安全问题由开发决定。然而,线程安全与否取决于是否有共享的资源竞争关系存在,Spring中单例Bean是否是线程安全取决于Bean是有状态的Bean还是无状态的Bean。

 

何为有状态Bean跟无状态Bean?

  • 有状态的Bean:有数据存储功能,eg:Bean中有User对象用于存储用户信息;
  • 无状态的Bean:无数据存储功能

 
下面是测试的Demo:

(1)MainUser.java:

@Data
@Builder
public class MainUser {

    private String id;

    private String sex;

    private String userName;

}

(2)SysMainController .java

@RequestMapping("/main")
@Controller
public class SysMainController {

    @Autowired
    private SysMainService mainService;

    @RequestMapping(value = "/test")
    public void mainMethod() {

        // thread-1
        new Thread(() -> {
            MainUser user = MainUser.builder()
                    .id("1")
                    .sex("男")
                    .userName("张三")
                    .build();
            mainService.testBean(user);
        }).start();

        // thread-2
        new Thread(() -> {
            MainUser user = MainUser.builder()
                    .id("2")
                    .sex("女")
                    .userName("小芳")
                    .build();
            mainService.testBean(user);
        }).start();

    }

}

(3)SysMainServiceImpl.java

@Service
public class SysMainServiceImpl implements SysMainService {

	/** 基本类型变量亦可 */
    private MainUser user; // 非定义为static的变量

    @Override
    public void testBean(MainUser user) {
        this.user = user;
        print();
    }

    public void print() {
        try {
            Thread.sleep(2000);
            System.out.println("ThreadName: " + Thread.currentThread().getName() + ", user: " + user);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

测试多次的结果:

ThreadName: Thread-57, user: MainUser(id=2, sex=, userName=小芳)
ThreadName: Thread-58, user: MainUser(id=2, sex=, userName=小芳)

 
结论:

  • 两个线程进入print()方法都会创建自己的私有VM,但是user对象是共享的,这点容易在开发中被忽略;
  • 故在Spring的单例模式的Bean中不应该在全局层面上定义有数据存储功能的对象 / 基本类型变量 / static变量,会产生线程安全问题;若要这么做,可以通过声明Bean的 Scope=‘prototype’ 或者使用本地变量 ThreadLocal 进行定义

 
以上为笔者遇到的问题进行的思考实验,若有不正确的地方欢迎指出~~~

评论 4 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:深蓝海洋 设计师:CSDN官方博客 返回首页

打赏作者

软件开发随心记

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值