当前位置:首页 > 科技  > 软件

面试官:单例Bean一定不安全吗?实际工作中如何处理此问题?

来源: 责编: 时间:2024-01-15 09:21:45 260观看
导读默认情况下,Spring Boot 中的 Bean 是非线程安全的。这是因为,默认情况下 Bean 的作用域是单例模式,那么此时,所有的请求都会共享同一个 Bean 实例,这意味着这个 Bean 实例,在多线程下可能被同时修改,那么此时它就会出现线程

wfq28资讯网——每日最新资讯28at.com

默认情况下,Spring Boot 中的 Bean 是非线程安全的。这是因为,默认情况下 Bean 的作用域是单例模式,那么此时,所有的请求都会共享同一个 Bean 实例,这意味着这个 Bean 实例,在多线程下可能被同时修改,那么此时它就会出现线程安全问题。wfq28资讯网——每日最新资讯28at.com

wfq28资讯网——每日最新资讯28at.com

Bean 的作用域(Scope)指的是确定在应用程序中创建和管理 Bean 实例的范围。也就是在 Spring 中,可以通过指定不同的作用域来控制 Bean 实例的生命周期和可见性。例如,单例模式就是所有线程可见并共享的,而原型模式则是每次请求都创建一个新的原型对象。wfq28资讯网——每日最新资讯28at.com

wfq28资讯网——每日最新资讯28at.com

1、单例Bean一定不安全吗?

并不是,单例 Bean 分为以下两种类型:wfq28资讯网——每日最新资讯28at.com

  • 无状态 Bean(线程安全):Bean 没有成员变量,或多线程只会对 Bean 成员变量进行查询操作,不会修改操作。
  • 有状态 Bean(非线程安全):Bean 有成员变量,并且并发线程会对成员变量进行修改操作。

所以说:有状态的单例 Bean 是非线程安全的,而无状态的 Bean 是线程安全的wfq28资讯网——每日最新资讯28at.com

wfq28资讯网——每日最新资讯28at.com

但在程序中,只要有一种情况会出现线程安全问题,那么它的整体就是非线程安全的,所以总的来说,单例 Bean 还是非线程安全的。wfq28资讯网——每日最新资讯28at.com

wfq28资讯网——每日最新资讯28at.com

(1)无状态的Bean

无状态的 Bean 指的是不存在成员变量,或只有查询操作,没有修改操作,它的实现示例代码如下:wfq28资讯网——每日最新资讯28at.com

import org.springframework.stereotype.Service;@Servicepublic class StatelessService {    public void doSomeTask() {        // 执行任务    }}

(2)有状态的Bean

有成员变量,并且存在对成员变量的修改操作,如下代码所示:wfq28资讯网——每日最新资讯28at.com

import org.springframework.stereotype.Service;@Servicepublic class UserService {    private int count = 0;    public void incrementCount() {        count++; // 非原子操作,并发存在线程安全问题    }    public int getCount() {        return count;    }}

2、如何保证线程安全?

想要保证有状态 Bean 的线程安全,可以从以下几个方面来实现:wfq28资讯网——每日最新资讯28at.com

  • 使用 ThreadLocal(线程本地变量):每个线程修改自己的变量,就没有线程安全问题了。
  • 使用锁机制:例如 synchronized 或 ReentrantLock 加锁修改操作,保证线程安全。
  • 设置 Bean 为原型作用域(Prototype):将 Bean 的作用域设置为原型,这意味着每次请求该 Bean 时都会创建一个新的实例,这样可以防止不同线程之间的数据冲突,不过这种方法增加了内存消耗。
  • 使用线程安全容器:例如使用 Atomic 家族下的类(如 AtomicInteger)来保证线程安全,此实现方式的本质还是通过锁机制来保证线程安全的,Atomic 家族底层是通过乐观锁 CAS(Compare And Swap,比较并替换)来保证线程安全的。

具体实现如下。wfq28资讯网——每日最新资讯28at.com

(1)使用ThreadLocal保证线程安全

实现代码如下:wfq28资讯网——每日最新资讯28at.com

import org.springframework.stereotype.Service;@Servicepublic class UserService {    private ThreadLocal<Integer> count = ThreadLocal.withInitial(() -> 0);    public void incrementCount() {        count.set(count.get() + 1);    }    public int getCount() {        return count.get();    }}

使用 ThreadLocal 需要注意一个问题,在用完之后记得调用 ThreadLocal 的 remove 方法,不然会发生内存泄漏问题。wfq28资讯网——每日最新资讯28at.com

(2)使用锁机制

锁机制中最简单的是使用 synchronized 修饰方法,让多线程执行此方法时排队执行,这样就不会有线程安全问题了,如下代码所示:wfq28资讯网——每日最新资讯28at.com

import org.springframework.stereotype.Service;@Servicepublic class UserService {    private int count = 0;    public synchronized void incrementCount() {        count++; // 非原子操作,并发存在线程安全问题    }    public int getCount() {        return count;    }}

(3)设置为原型作用域

原型作用域通过 @Scope("prototype") 来设置,表示每次请求时都会生成一个新对象(也就没有线程安全问题了),如下代码所示:wfq28资讯网——每日最新资讯28at.com

import org.springframework.stereotype.Service;@Service@Scope("prototype")public class UserService {    private int count = 0;    public void incrementCount() {        count++; // 非原子操作,并发存在线程安全问题    }    public int getCount() {        return count;    }}

(4)使用线程安全容器

我们可以使用线程安全的容器,例如 AtomicInteger 来替代 int,从而保证线程安全,如下代码所示:wfq28资讯网——每日最新资讯28at.com

import org.springframework.stereotype.Service;import java.util.concurrent.atomic.AtomicInteger;@Servicepublic class UserService {    private AtomicInteger count = new AtomicInteger(0);    public void incrementCount() {        count.incrementAndGet();    }    public int getCount() {        return count.get();    }}

实际工作中如何保证线程安全?

实际工作中,通常会根据具体的业务场景来选择合适的线程安全方案,但是以上解决线程安全的方案中,ThreadLocal 和原型作用域会使用更多的资源,占用更多的空间来保证线程安全,所以在使用时通常不会作为最佳考虑方案。wfq28资讯网——每日最新资讯28at.com

而锁机制和线程安全的容器通常会优先考虑,但需要注意的是 AtomicInteger 底层是乐观锁 CAS 实现的,因此它存在乐观锁的典型问题 ABA 问题(如果有状态的 Bean 中既有 ++ 操作,又有 -- 操作时,可能会出现 ABA 问题),此时就要使用锁机制,或 AtomicStampedReference 来解决 ABA 问题了。wfq28资讯网——每日最新资讯28at.com

小结

单例模式的 Bean 并不一定都是非线程安全的,其中有状态的 Bean 是存在线程安全问题的。实际工作中通常会使用锁机制(synchronized 或 ReentrantLock)或线程安全的容器来解决 Bean 的线程安全问题,但具体使用哪种方案,还要结合具体业务场景来定。wfq28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-60980-0.html面试官:单例Bean一定不安全吗?实际工作中如何处理此问题?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Go语言常见错误—Any 没传递任何信息

下一篇: 使用Java 17中的 record 替代 Lombok 的部分功能

标签:
  • 热门焦点
  • 小米降噪蓝牙耳机Necklace分享:听一首歌 读懂一个故事

    在今天下午的小米Civi 2新品发布会上,小米还带来了一款新的降噪蓝牙耳机Necklace,我们也在发布结束的第一时间给大家带来这款耳机的简单分享。现在大家能见到最多的蓝牙耳机
  • 6月iOS设备好评榜:第一蝉联榜首近一年

    作为安兔兔各种榜单里变化最小的那个,2023年6月的iOS好评榜和上个月相比没有任何排名上的变化,仅仅是部分设备好评率的下降,长年累月的用户评价和逐渐退出市场的老款机器让这
  • 8月总票房已突破10亿!《封神》第一:口碑已经成了

    8月5日消息,据灯塔专业版数据,截至8月5日9时35分,8月总票房(含预售)已突破10亿。其中,《封神》以大比分的优势领先。根据官方消息,目前该片总票房已经超过14.
  • 摸鱼心法第一章——和配置文件说拜拜

    为了能摸鱼我们团队做了容器化,但是带来的问题是服务配置文件很麻烦,然后大家在群里进行了“亲切友好”的沟通图片图片图片图片对比就对比,简单对比下独立配置中心和k8s作为配
  • 不容错过的MSBuild技巧,必备用法详解和实践指南

    一、MSBuild简介MSBuild是一种基于XML的构建引擎,用于在.NET Framework和.NET Core应用程序中自动化构建过程。它是Visual Studio的构建引擎,可在命令行或其他构建工具中使用
  • 多线程开发带来的问题与解决方法

    使用多线程主要会带来以下几个问题:(一)线程安全问题  线程安全问题指的是在某一线程从开始访问到结束访问某一数据期间,该数据被其他的线程所修改,那么对于当前线程而言,该线程
  • 一个注解实现接口幂等,这样才优雅!

    场景码猿慢病云管理系统中其实高并发的场景不是很多,没有必要每个接口都去考虑并发高的场景,比如添加住院患者的这个接口,具体的业务代码就不贴了,业务伪代码如下:图片上述代码有
  • 阿里大调整

    来源:产品刘有媒体报道称,近期淘宝天猫集团启动了近年来最大的人力制度改革,涉及员工绩效、层级体系等多个核心事项,目前已形成一个初步的&ldquo;征求意见版&rdquo;:1、取消P序列
  • 华为将推出盘古数字人大模型 可帮助用户12小时完成数字人生成

    在今日举行的2023年华为云数字文娱AI创新峰会上,华为云全球Marketing与销售服务总裁石冀琳表示,华为云将在后续推出盘古数字人大模型,可帮助用户12小
Top