如何避免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();
    }
}
相关推荐
zfoo-framework5 分钟前
1.ansible安装 2.虚拟机克隆
java
码上解惑15 分钟前
从 Dify 工作流说起:常用节点怎么选、怎样组合?
java·人工智能·ai·agent·dify·智能体·spring ai
小大宇43 分钟前
python flask框架 SSE流式返回、跨域、报错
开发语言·python·flask
青山木1 小时前
Hot 100 --- 岛屿数量
java·数据结构·算法·leetcode·深度优先·广度优先
柒星栈1 小时前
PHP 源码怎么加密防破解?三套方案实战指南
开发语言·php·android studio
勉灬之1 小时前
Next.js + Prisma 跨平台部署踩坑记
开发语言·javascript·ecmascript
程序员-珍1 小时前
报错下载android sdk失败
android·java
糖果店的幽灵2 小时前
langgraph分支之 - 动态分支(Dynamic Branch)
java·前端·javascript·人工智能·langgraph
吃饱了得干活2 小时前
亿级订单表分库分表设计,从0到1全流程
java·数据库·面试
这是个栗子2 小时前
前端开发中的常用工具函数(九)
开发语言·javascript·ecmascript·at