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
相关产品推荐
相关产品推荐

