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

news2024/11/25 7:06:03

一、概述

        我们知道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();
        }
    }
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【挑战业余一周拿证】一、亚马逊云科技简介 - 第 1 节 - 模块 1 简介

CSDN 官方中文视频&#xff08;免费&#xff09;&#xff1a;点击进入 一、亚马逊云科技简介 第 1 节 - 模块 1 简介 1、讲师&#xff1a;李锦鸿 部门&#xff1a;亚马逊云科技培训与认证部门 方向&#xff1a;从事数据中心及云计算相关产品与解决方案工作 课程&#xff…

【Linux系统编程】冯 • 诺依曼体系结构(什么是冯 • 诺依曼体系结构?冯 • 诺依曼体系结构如何应用?)

目录 一、前言 二、什么是冯 • 诺依曼体系结构&#xff1f; &#x1f4a6; 冯 • 诺依曼体系结构的发展推导 &#x1f4a6;冯 • 诺依曼体系结构的5大部件 ⭐输入和输出设备 ⭐存储器 ⭐中央处理器&#xff08;CPU&#xff09; &#x1f4a6;冯 • 诺依曼体系结构的细节…

2016年五一杯数学建模A题购房中的数学问题解题全过程文档及程序(采光与房款)

2016年五一杯数学建模 A题 购房中的数学问题 原题再现 随着现代社会经济的快速发展&#xff0c;房地产成为国家经济发展中重要的经济增长点之一。为了充分利用楼房建设的土地面积&#xff0c;开发商经常会选择建筑高层住宅。在购买住房时&#xff0c;影响消费者选择购房的因素…

设计模式—依赖倒置原则(DIP)

1.概念 依赖倒置原则&#xff08;Dependence Inversion Principle&#xff09;是程序要依赖于抽象接口&#xff0c;不要依赖于具体实现。简单的说就是要求对抽象进行编程&#xff0c;不要对实现进行编程&#xff0c;这样就降低了客户与实现模块间的耦合。 通俗的讲&#xff1…

elastic -job和springboot集成实现分布式调度5

一 案例介绍说明 1.1 案例介绍 基于 Spring boot 集成方式的而产出的工程代码&#xff0c;完成对作业分片的实现&#xff0c;文件数据备份采取更接近真实项目的数 据库存取方式。 1.分片设置 2.每个线程获取给自的类型 二 操作说明 2.1 数据表的初始化 DROP TABLE IF EXIS…

【算法萌新闯力扣】:回文链表

力扣题目&#xff1a;回文链表 开篇 今天是备战蓝桥杯的第23天。我加入的编程导航算法通关村也在今天开营啦&#xff01;那从现在起&#xff0c;我的算法题更新会按照算法村的给的路线更新&#xff0c;更加系统。大家也可以关注我新开的专栏“算法通关村”。里面会有更全面的知…

VMware安装部署kail镜像服务器【详细包含百度云盘镜像】

VMware安装部署kail镜像服务器【详细包含百度云盘镜像】 kail是一个很好玩的操作系统&#xff0c;不多说了哈 下载kail镜像 kail官网:https://www.kali.org/get-kali/#kali-platforms 百度云盘下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1PRjoP_1v8DEZ7-dA_…

python桌面开发PyQt6库和工具库QTDesigner安装和配置

一、安装PyQt6 二、安装pyqt6-tools 三、安装外部工具 四、创建QTDesigner 1.首先查找designer.exe的路径&#xff08;可以自己在窗口中选择&#xff0c;也可以使用Everything搜索&#xff09; 2.使用Everything搜索后会出现多个designer.exe,选中&#xff0c;OpenPath 3.选择…

讯飞星火知识库文档问答Web API的使用(二)

上一篇提到过星火spark大模型&#xff0c;现在有更新到3.0&#xff1a; 给ChuanhuChatGPT 配上讯飞星火spark大模型V2.0&#xff08;一&#xff09; 同时又看到有知识库问答的web api&#xff0c;于是就测试了一下。 下一篇是在ChuanhuChatGPT 中单独写一个基于星火知识库的内容…

【Web-Note】 JavaScript概述

JavaSript基本语法 JavaSript程序不能独立运行&#xff0c;必须依赖于HTML文件。 <script type "text/javascript" [src "外部文件"]> JS语句块; </script> script标记是成对标记。 type属性&#xff1a;说明脚本的类型。 "text/jav…

rdf-file:分布式环境下的文件处理

一&#xff1a;数据量大了以后&#xff0c;单机解析或者生成文件的效率就很低&#xff0c;需要通过集群处理 机构过来的文件&#xff1a;我们先对文件进行分片&#xff0c;在利用集群集群处理分片文件。给机构文件&#xff1a;分库分表数据&#xff0c;每个分表生成一个分片文…

什么是分布式锁?Redis实现分布式锁详解

目录 前言&#xff1a; 分布式系统买票示例 引入redis做分布式锁 引入过期时间 引入校验id 引入lua脚本 过期时间续约问题 redlock算法 小结&#xff1a; 前言&#xff1a; 在分布式系统中&#xff0c;涉及多个主机访问同一块资源&#xff0c;此时就需要锁来做互斥控制…

Linux篇:文件系统

一、共识原理&#xff1a; 文件文件内容文件属性 磁盘上存储文件存文件的内容&#xff08;数据块&#xff09;存文件的属性&#xff08;inode&#xff09; Linux的文件在磁盘中存储是将属性和内容分开存储的。 二、硬件简述&#xff1a; 1. 认识硬件 磁盘&#xff1a;唯一的一…

Vue+ElementUI技巧分享:周数选择器

文章目录 前言一、周数的计算逻辑1.1 周数的定义1.2 年初周数的确定1.3 周数的计算方法 二、代码实现2.1 计算周数2.2 获取周的日期范围2.3 根据周数获取日期范围2.4 控件引用2.4.1 控件引用代码分析2.4.2 初始化变量代码分析 2.5 周数选择器完整代码 总结 前言 Vue.js和Eleme…

文心一言 VS 讯飞星火 VS chatgpt (143)-- 算法导论12.1 3题

三、用go语言&#xff0c;设计一个执行中序遍历的非递归算法。&#xff08;提示:一种容易的方法是使用栈作为辅助数据结构;另一种较复杂但比较简洁的做法是不使用栈&#xff0c;但要假设能测试两个指针是否相等。&#xff09; 文心一言&#xff0c;代码正常运行&#xff1a; …

Linux 面试题(一)

目录 1、绝对路径用什么符号表示&#xff1f;当前目录、上层目录用什么表示&#xff1f;主目录用什么表示? 切换目录用什么命令&#xff1f; 2、怎么查看当前进程&#xff1f;怎么执行退出&#xff1f;怎么查看当前路径&#xff1f; 3、怎么清屏&#xff1f;怎么退出当前命…

4G模块(EC600N)通过MQTT连接华为云

目录 一、前言 二、EC600N模块使用 1&#xff0e;透传模式 2&#xff0e;非透传模式 3、华为云的MQTT使用教程&#xff1a; 三、具体连接步骤 1、初始化检测 2、打开MQTT客户端网络 3、创建产品 4、创建模型 5、注册设备 6、连接客户端到MQTT服务器 7、发布主题消…

【数据分享】我国12.5米分辨率的坡向数据(免费获取)

地形数据&#xff0c;也叫DEM数据&#xff0c;是我们在各项研究中最常使用的数据之一。之前我们分享过源于NASA地球科学数据网站发布的12.5米分辨率DEM地形数据&#xff01;基于该数据我们处理得到12.5米分辨率的坡度数据、12.5米分辨率的山体阴影数据&#xff08;均可查看之前…

Python 安装mysqlclient 错误 无法打开包括文件: “mysql.h”: 解决方法

解决方案&#xff1a;python最新3.12.0不支持mysqlclient 请下载 python3.9.9 版本 高速下载地址CNPM Binaries Mirror 官方下载地址Welcome to Python.org 下载完成后将python添加到环境变量 pycharm 虚拟环境下的python版本切换到你刚才下载的3.9.9的python版本 Avai…