You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring单例Bean含StringBuilder时,如何实现线程安全且高性能?

多线程下Spring单例Bean处理实例变量的高性能方案

你的核心问题是单例Bean的共享StringBuilder引发线程安全问题,用synchronized会导致全局锁竞争,拖垮性能。下面给你几个兼顾线程安全和性能的实用方案:

方案一:用ThreadLocal实现线程隔离(最适合你的场景)

ThreadLocal会给每个线程分配独立的StringBuilder实例,线程间完全隔离,不需要任何锁,性能和单线程几乎一致。刚好匹配你需要跨appLogger和aggregateLogger方法聚合同一线程内日志的需求。

修改代码如下:

@Aspect
@Slf4j
@Component
public class A{
    // 每个线程拥有独立的StringBuilder实例
    private ThreadLocal<StringBuilder> threadLocalBuilder = ThreadLocal.withInitial(StringBuilder::new);

    public StringBuilder getSb(){
        return threadLocalBuilder.get();
    }

    public void appendToSb(String s){
        threadLocalBuilder.get().append(s);
    }

    @Around("@annotation(execTimeLogger)")
    public Object appLogger(ProceedingJointPoint pjp) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object object = pjp.proceed();
        long endTime = System.currentTimeMillis();
        String str = "Hello";
        appendToSb(str);
        return object;
    }

    @Around("@annotation(logger)")
    public Object aggregateLogger(ProceedingJointPoint pjp) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object object = pjp.proceed();
        long endTime = System.currentTimeMillis();
        String str = "World";
        appendToSb(str);
        log.info(getSb().toString()); 
        // 清空当前线程的builder,线程池场景建议用完后remove避免内存泄漏
        threadLocalBuilder.get().setLength(0);
        // threadLocalBuilder.remove();
        return object;
    }
}

注意:如果Spring用线程池处理请求(默认行为),线程会被复用,用完后要么清空builder,要么调用remove()清理ThreadLocal中的实例,防止内存泄漏。

方案二:将StringBuilder改为方法局部变量(适合无需跨方法共享的场景)

如果你的日志聚合不需要跨appLogger和aggregateLogger方法,直接在每个切面方法里创建局部的StringBuilder,完全不存在线程安全问题,也没有任何锁开销:

@Around("@annotation(execTimeLogger)")
public Object appLogger(ProceedingJointPoint pjp) throws Throwable {
    long startTime = System.currentTimeMillis();
    Object object = pjp.proceed();
    long endTime = System.currentTimeMillis();
    StringBuilder builder = new StringBuilder();
    builder.append("Hello");
    // 直接使用或传递(如果需要)
    return object;
}

此方案不适合你当前跨方法聚合日志的需求,仅作扩展参考。

方案三:使用线程安全的替代类(不推荐,性能不如ThreadLocal)

如果一定要用共享变量,可以用StringBuffer替代StringBuilder,它的所有方法都是同步的,但本质还是加锁,只是锁粒度比全局synchronized小一点,性能提升有限,依然存在锁竞争,不如ThreadLocal高效。


内容的提问来源于stack exchange,提问作者dev333

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 03:33:36