spring单例bean导致的bug

spring创建bean时默认都是单例的,通过DefaultSingletonBeanRegistry.java#getSingleton方法添加到map中缓存,之后我们获取到的都是单例bean,但是这样是存在线程问题的。比如单例bean存在某个和线程相关的属性,在不同线程中取值不同的,这些值都是根据用户传参决定的。例如,商城用户是个男性,则喜欢看电子类产品;如果是个女性则喜欢化妆品。这时可能会出现一种情况,单例bean发现登录商城的是个男性,而同一时间有个女性也登陆了商城,单例bean于是把男性改成女性,向男性推荐化妆品。结合代码看下吧,

@RestController
@RequestMapping("/person")
public class SingletonBeanController {
    private String iSex;

    private Map<String, String> map = new HashMap(){{
        put("man", "short hair, 180");
        put("woman", "long hair, big eye");
    }
    };

    @GetMapping("{sex}")
    String test(@PathVariable String sex){
        this.iSex = sex;
        return map.get(iSex);
    }
}

最初这是没有问题的,但是随着业务量增加,test()方法变成这样,sleep()表示中间进行了很多业务操作。

    @GetMapping("{sex}")
    String test(@PathVariable String sex){
        this.iSex = sex;
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return map.get(iSex);
    }

此时,如果有两个用户同时调用接口,第一个男性用户时iSex为"man",第二个女性用户时iSex为"woman",但是第二个女性用户会把第一个男性用户的iSex改为"woman",因为这是单例bean,在高并发时很容易出现这种情况,结果就是男性用户看到的全是女性商品。解决办法也很简单,可以通过@Scope定义当前bean为原型bean,@Scope(value = BeanDefinition.SCOPE_PROTOTYPE),但不是很建议,因为这样每次都创建,也可以通过ThreadLocal线程变量来解决。

    private ThreadLocal<String> iSex = new ThreadLocal<>();
    @GetMapping("{sex}")
    String test(@PathVariable String sex){
        this.iSex.set(sex);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return map.get(iSex.get());
    }

如果这个属性类型是简单得类型,不会占用太多内存,很容易生成得话,大可以定义个方法内局部变量。

    @GetMapping("/shop/{sex}")
    String sex(@PathVariable String sex){
        String iSex = sex;
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return map.get(iSex);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值