系列十八、Spring bean线程安全问题

一、概述

        我们知道Spring中的bean,默认情况下是单例的,那么Spring中的bean是线程安全的吗?这个需要分情况考虑,bean中是否存在成员变量?bean中的成员变量是怎么处理的?...,针对bean的状态会有不同的处理方案:

        情况一:bean是单例的;

        情况二:bean是多例的(不会存在线程安全问题);

出现线程安全问题的原因:单实例bean中存在成员变量,并且有对这个bean进行读写的操作,因此出现了线程安全的问题。

二、演示Spring bean存在线程安全

2.1、UserService

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/26 14:55
 * @Description: 
 */
@Service
public class UserService {

    private String username;

    public String welcome(String name) {
        username = "welcome " + name;
        try {Thread.sleep(100);} catch (Exception e) {e.printStackTrace();}
        return username;
    }

}

2.2、MySpringConfig

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/23 15:29
 * @Description:
 */
@Configuration
@ComponentScan(basePackages = {"org.star"})
public class MySpringConfig {

}

2.3、AopFullAnnotationMainApp

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/23 15:14
 * @Description:
 */
@Slf4j
public class AopFullAnnotationMainApp {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
        UserService userService = context.getBean(UserService.class);
        Random r = new Random();
        String[] nameArray = new String[]{"张三", "李四", "王五", "赵六", "钱七"};
        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                int index = r.nextInt(5);
                String name = nameArray[index];
                log.info("当前线程:{},当前索引:{},当前name的值:{},当前取出的值:{}", Thread.currentThread().getName(), index, name, userService.welcome(name));
            }, "线程" + i).start();
        }
    }

}

三、解决方法

        上面代码演示了Spring中的bean的确存在着线程安全问题,出现问题我们要解决问题,针对Spring中bean中存在的线程安全,我们可以通过以下方式进行解决:

        方案一:将成员变量修改为局部变量(单例bean);

        方案二:使用ThreadLocal(单例bean);

        方案三:使用同步锁synchronized(单例bean);

        方案四:将单例bean设置为多例的;

        案例代码如下:

3.1、将成员变量修改为局部变量(单例bean)

3.1.1、UserService2

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/26 14:55
 * @Description: 单例bean线程不安全(解决方式一:将成员变量修改为局部变量)
 */
@Service
public class UserService2 {

    public String welcome(String name) {
        String username = "welcome " + name;
        try {Thread.sleep(100);} catch (Exception e) {e.printStackTrace();}
        return username;
    }

}

3.1.2、MySpringConfig(同上)

3.1.3、AopFullAnnotationMainApp 

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/23 15:14
 * @Description:
 */
@Slf4j
public class AopFullAnnotationMainApp {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);

        UserService2 userService2 = context.getBean(UserService2.class);
        Random r = new Random();
        String[] nameArray = new String[]{"张三", "李四", "王五", "赵六", "钱七"};
        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                int index = r.nextInt(5);
                String name = nameArray[index];
                log.info("当前线程:{},当前索引:{},当前name的值:{},当前取出的值:{}", Thread.currentThread().getName(), index, name, userService2.welcome(name));
            }, "线程" + i).start();
        }
    }
}

3.2、使用ThreadLocal(单例bean)

3.2.1、UserService3

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/26 14:55
 * @Description: 单例bean线程不安全(解决方式二:使用ThreadLocal)
 */
@Service
public class UserService3 {

    private ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public String welcome(String name) {
        threadLocal.set("welcome" + name);
        try {Thread.sleep(100);} catch (Exception e) {e.printStackTrace();}
        return threadLocal.get();
    }

}

3.2.2、MySpringConfig(同上)

3.2.4、AopFullAnnotationMainApp

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/23 15:14
 * @Description:
 */
@Slf4j
public class AopFullAnnotationMainApp {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);

        UserService3 userService3 = context.getBean(UserService3.class);
        Random r = new Random();
        String[] nameArray = new String[]{"张三", "李四", "王五", "赵六", "钱七"};
        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                int index = r.nextInt(5);
                String name = nameArray[index];
                log.info("当前线程:{},当前索引:{},当前name的值:{},当前取出的值:{}", Thread.currentThread().getName(), index, name, userService3.welcome(name));
            }, "线程" + i).start();
        }

    }
}

3.3、使用同步锁synchronized(单例bean)

3.3.1、UserService4

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/26 14:55
 * @Description: 单例bean线程不安全(解决方式三:使用同步锁synchronized)
 */
@Service
public class UserService4 {

    private String username;

    public synchronized String welcome(String name) {
        username = "welcome " + name;
        try {Thread.sleep(100);} catch (Exception e) {e.printStackTrace();}
        return username;
    }

}

3.3.2、MySpringConfig(同上)

3.3.3、AopFullAnnotationMainApp 

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/23 15:14
 * @Description:
 */
@Slf4j
public class AopFullAnnotationMainApp {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);

        UserService4 userService4 = context.getBean(UserService4.class);
        Random r = new Random();
        String[] nameArray = new String[]{"张三", "李四", "王五", "赵六", "钱七"};
        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                int index = r.nextInt(5);
                String name = nameArray[index];
                log.info("当前线程:{},当前索引:{},当前name的值:{},当前取出的值:{}", Thread.currentThread().getName(), index, name, userService4.welcome(name));
            }, "线程" + i).start();
        }
    }
}

3.4、将单例bean设置为多例的

3.4.1、UserService5

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/26 14:55
 * @Description: 单例bean线程不安全(解决方式四:将单例bean设置为多例的)
 */
@Scope("prototype")
@Service
public class UserService5 {

    private String username;

    public String welcome(String name) {
        username = "welcome " + name;
        try {Thread.sleep(100);} catch (Exception e) {e.printStackTrace();}
        return username;
    }

}

3.4.2、MySpringConfig(同上)

3.4.3、AopFullAnnotationMainApp 

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/23 15:14
 * @Description:
 */
@Slf4j
public class AopFullAnnotationMainApp {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);

        Random r = new Random();
        String[] nameArray = new String[]{"张三", "李四", "王五", "赵六", "钱七"};
        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                UserService5 userService5 = context.getBean(UserService5.class);
                int index = r.nextInt(5);
                String name = nameArray[index];
                log.info("当前线程:{},当前索引:{},当前name的值:{},当前取出的值:{}", Thread.currentThread().getName(), index, name, userService5.welcome(name));
            }, "线程" + i).start();
        }
    }
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/191154.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenGL的学习之路 -5

1.视景体 正交投影 人眼看世界&#xff0c;有一个可见范围。范围内可见&#xff0c;范围外不可见。视景体就是这么一个概念。 &#xff08;上图仅学习记录用&#xff09; 在OGL中&#xff0c;有两种投影方式&#xff0c;对应两种视景体。第一种&#xff0c;正交投影&#xf…

Flask学习二:项目拆分、请求与响应、cookie

教程 教程地址&#xff1a; 千锋教育Flask2框架从入门到精通&#xff0c;Python全栈开发必备教程 老师讲的很好&#xff0c;可以看一下。 项目拆分 项目结构 在项目根目录下&#xff0c;创建一个App目录&#xff0c;这是项目下的一个应用&#xff0c;应该类似于后端的微服…

案例029:基于微信小程序的阅读网站设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

为什么PCB板大多数都是绿色的?

什么时候开始接触到PCB板的呢&#xff1f;也许是是把家里的电视遥控器拆开的时候&#xff0c;也许是你的小霸王学习机游戏手柄给按坏拆开的时候&#xff0c;也许那时候你还不知道这叫PCB电路板。然后就是大学里使用嘉立创免费打板的时候&#xff0c;有一个选项绿色板还是黑色板…

自建CA实战之 《0x01 Nginx 配置 https单向认证》

自建私有化证书颁发机构&#xff08;Certificate Authority&#xff0c;CA&#xff09;实战之 《0x01 Nginx 配置 https单向认证》 上一篇文章我们介绍了如何自建私有化证书颁发机构&#xff08;Certificate Authority&#xff0c;CA&#xff09;&#xff0c;本篇文章我们将介…

单细胞seurat入门—— 从原始数据到表达矩阵

根据所使用的建库方法&#xff0c;单细胞的RNA序列&#xff08;也称为读取&#xff08;reads&#xff09;或标签&#xff08;tags&#xff09;&#xff09;将从转录本的3端&#xff08;或5端&#xff09;&#xff08;10X Genomics&#xff0c;CEL-seq2&#xff0c;Drop-seq&…

Java 文件常用操作与流转换

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

SATA信息传输FIS结构总结通过实例代码(快速)掌握(二)

目录 一、简介二、总体介绍2.1 详细FIS传输过程2.2 FIS内容详解 三、FIS实例3.1 构造一个write FIS3.1.1 WRITE FIS内容3.1.2 协议分析仪trace分析3.1.3 write过程总trace 3.2 构造一个read FIS3.2.1 READ FIS内容3.2.2 协议分析仪 read trace3.2.3 read过程总trace 3.3 FIS和C…

Linux系统管理:WinSCP 安装与使用

目录 一、实验 1.下载WinSCP 2.安装WinSCP 3.使用WinSCP 一、实验 1.下载WinSCP &#xff08;1&#xff09;地址 Downloading WinSCP-6.1.2-Setup.exe :: WinSCP 2.安装WinSCP &#xff08;1&#xff09;选择安装程序模式 &#xff08;2&#xff09;点击 &#xff08;3…

C++初识类和对象

前言 上一期我们介绍了一些C入门的基础知识&#xff0c;本期我们来介绍面向对象。初步认识一下面向对象和面向过程、类、以及封装&#xff01; 本期内容介绍 面向过程和面向对象 类的引入 类的定义 类的访问限定符和封装 类的作用域 类的实例化 类对象模型 this指针 一、面向…

name 属性:提高 Vue 应用可维护性的关键

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

Linux加强篇006-存储结构与管理硬盘

目录 前言 1. 从“/”开始 2. 物理设备命名规则 3. 文件系统与数据资料 4. 挂载硬件设备 5. 添加硬盘设备 6. 添加交换分区 7. 磁盘容量配额 8. VDO虚拟数据优化 9. 软硬方式链接 前言 悟已往之不谏&#xff0c;知来者之可追。实迷途其未远&#xff0c;觉今是而昨非…

5.7 Windows驱动开发:取进程模块函数地址

在笔者上一篇文章《内核取应用层模块基地址》中简单为大家介绍了如何通过遍历PLIST_ENTRY32链表的方式获取到32位应用程序中特定模块的基地址&#xff0c;由于是入门系列所以并没有封装实现太过于通用的获取函数&#xff0c;本章将继续延申这个话题&#xff0c;并依次实现通用版…

linux内核态内存泄漏检测-kmemleak

参考文章&#xff1a;Linux内核态内存泄漏检测工具--kmemleak工具原理及应用_linux 内存泄漏检测工具-CSDN博客 细说&#xff5c;Linux 内存泄漏检测实现原理与实现_内核_指针_信息 kmemleak原理&#xff1a;看网上说大概原理是在通过kmalloc&#xff0c;vmalloc&#xff0c…

分布式锁,分布式锁应该具备哪些条件,分布式锁的实现方式有:基于Zookeeper实现、Redis实现、数据库实现

文章目录 分布式锁0-1分布式锁--包含CAP理论模型概述分布式锁&#xff1a;分布式锁应该具备哪些条件&#xff1a;分布式锁的业务场景&#xff1a; 分布式锁的实现方式有&#xff1a;基于Zookeeper - 分布式锁实现思想优缺点基于Redis - 分布式锁实现思想实现思想的具体步骤&…

[AutoSAR存储] 车载存储层次 和 常用存储芯片概述

公知及经验整理&#xff0c;原创保护&#xff0c;禁止转载。 专栏 《AutoSAR存储》 <<<< 返回总目录 <<<< 1 存储系统层次 先抛个问题&#xff0c; 为什么要划分存储器的层次&#xff1f; 速度越快&#xff0c;但成本越高&#xff0c;从经济的角度规…

【Web】[GKCTF 2021]easycms

直接点击登录按钮没有反应 扫目录扫出来/admin.php 访问 弱口令admin 12345直接登录成功 点开设计--主题--自定义 编辑页头&#xff0c;类型选择php源代码 点保存显示权限不够 设计--组件--素材库 先随便上传一个文件&#xff0c;之后改文件名称为../../../../../system/tmp…

OSS+CDN的资费和安全

文章目录 花费OSSCDNOSS CDN 安全OSS防盗链跨域设置CORS数据加密 CDN防盗链URL鉴权Cookie鉴权远程鉴权IP黑白名单UA黑白名单 回源服务自定义私有参数IP黑白名单数据加密 花费 OSS 存储费用 &#xff1a;0.12元/GB/月下行流量费用 &#xff1a;0.5元/GB请求费用 &#xff1a;…

2023金盾杯线上赛-AGRT战队-WP

目录 WEB ApeCoin get_source ezupload easyphp MISC 来都来了 芙宁娜 Honor Crypto 我看看谁还不会RSA hakiehs babyrsa PWN sign-format RE Re1 WEB ApeCoin 扫描发现有源码泄露&#xff0c;访问www.tar.gz得到源码。 在源码中发现了冰蝎马。 Md5解码&am…

持续集成部署-k8s-配置与存储-存储类:动态创建NFS-PV案例

动态创建NFS-PV案例 1. 前置条件2. StorageClass 存储类的概念和使用3. RBAC 配置4. storageClass 配置5. 创建应用&#xff0c;测试 PVC 的自动配置6. 解决 PVC 为 Pending 状态问题7. 单独测试自动创建 PVC 1. 前置条件 这里使用 NFS 存储的方式&#xff0c;来演示动态创建 …