如何避免Bean的线程安全问题

通常情况下Bean是基于单例模式创建的对象,不存在线性安全问题。但是如果Bean对象中存在公有的变量被多线程调用将会导致变量数据错乱。

复制代码
@Service
public class UserService {
    private int count = 0;
    public void add() {
        count++; // 多线程下计数错误
    }
}

有以下4种解决方案:

1、设计成无状态的Bean:Bean对象中不存放可变的实例变量,通过内部方法修改变量值。

复制代码
// 安全写法:无状态 Bean
@Service
public class UserService {
    // 没有任何可修改的成员变量

    public int add(int num) {
        // 局部变量,线程安全
        int count = 0;
        count = count + num;
        return count;
    }
}

2、改用原型作用域

@Scope(""),每次getBean都会创建一个新的实例

3、bean中用线程安全的数据结构

CuncurrentHashMap代替HashMap,让每个Bean都加上锁。同样也可以手动上synchronized锁。

4、用ThreadLocal:

每个线程维护自己的变量副本互补干扰。

复制代码
@Service
public class UserService {
    // 线程独立存储
    private ThreadLocal<Integer> countLocal = ThreadLocal.withInitial(() -> 0);

    public void add() {
        int count = countLocal.get();
        countLocal.set(count + 1);
    }

    // 用完必须 remove,防止内存泄漏
    public void clear() {
        countLocal.remove();
    }
}

注意每个线程都需要通过remove()方法清理线程。防止未清理情况下复用线程导致数据错乱。

企业开发中通过全局配置AOP拦截方法进行清理。

复制代码
@Aspect
@Component
public class ThreadLocalCleanAspect {

    // 拦截所有 @Service、@Controller 方法
    @Pointcut("execution(* com.xxx..service.*.*(..)) || execution(* com.xxx..controller.*.*(..))")
    public void servicePointcut() {}

    // 【最终通知】无论方法正常/异常,都执行清理
    @After("servicePointcut()")
    public void cleanThreadLocal() {
        ThreadContext.clear();
    }
}
相关推荐
ym hyd 1118 分钟前
宝宝早教服务系统源码 Java+SpringBoot+Vue3 前后分离
java·开发语言·spring boot
看浪的路人17 分钟前
第7讲:实时告警与自动化响应
开发语言·后端·golang
自强的小白23 分钟前
微服务之间的依赖传递和调用(实体类的模块传递以及负载均衡的调用!)注册中心
java·微服务
她说彩礼65万29 分钟前
C语言 单引号与双引号
java·服务器·c语言
知守观30 分钟前
百万级数据导出OOM:POI的坑与EasyExcel的流式写入实战(附内存对比)
java·后端
“AI国潮设计-小江”33 分钟前
Python实战 | SDXL批量生成“潮汕英歌舞”国潮甜品IP,附核心Prompt与商用授权思路
开发语言·人工智能·python·prompt·aigc
anew___37 分钟前
《从零手写操作系统 (19):Ext2文件系统实战——从内存到磁盘的跨越》
java·服务器·前端·数据库·算法
H.莓飛44 分钟前
【数据结构】栈
linux·开发语言·数据结构·算法·centos
无糖可乐没有灵魂1 小时前
用一篇文章来精通“进程/线程/协程/锁“多并发任务
开发语言·php
2601_962885721 小时前
如何用 Python 分析股票的日历效应?(星期效应与月份效应,附数据挖掘陷阱)
开发语言·python·数据挖掘