【单例模式更改属性导致的并发异常】

项目场景:

为了增加代码的代码可拓展性,降低耦合性。封装了一个根据不同的参数,可以调用不同的处理器(action)的框架。原本一切正常的代码,却在性能测试中出现了异常。是什么导致原本正常的代码在并发下却出现了异常呢?本文将探讨单例更改属性对并发的影响。


问题描述

@Service
public class TestServiceImpl implements TestService {

    public String test() {
        //做一个集合模拟入参
        List<String> testList = new ArrayList<String>();
        Collections.addAll(testList,"A","B","C","D","E");
        //遍历集合
        for (String s : testList) {
            //①获取process(处理器action封装在此对象中)
            TestProcess process = ProcessBeanFactory.getProcess(s);
            //②执行
            String result = process.execute();
            //③把结果存到ThreadLocal中
            YukinoThreadLocal.add(s,result);
        }
        //removeTheadLocal
        YukinoThreadLocal.clear();
        //返回
        return "success";
    }

}

示例是一段简单的代码,模拟了根据页面传来不同的参数来调取不同的处理器,以达到通过页面来控制CRUD的功能,降低了代码的耦合性,增加可拓展性。

这样一段简单的代码却在并发运行下出现了错误,ThreadLocal中储存的键和值出现了不匹配的问题。

原因分析:

单例模式更改属性,导致在并发下出现了错误。

我们先来看一下ProcessBeanFactory是如何获得process的:

@Component
public class ProcessBeanFactory {

    public static TestProcess getProcess(String s){
        //获得process对象(处理器action封装在此对象中)
        TestProcess process = YukinoContext.getBean(TestProcess.class);
        //根据参数s获取用于具体执行的action
        YukinoAction action = YukinoContext.getBean(s, YukinoAction.class);
        //封装bean对象里用于执行的属性
        process.setAction(action);
        //返回
        return process;
    }

}

再来看一下TestProcess 内部是如何封装和调用处理器action的:

@Component
public class TestProcessImplA implements TestProcess {

    private YukinoAction yukinoAction;

    //封装处理器
    public void setAction(YukinoAction yukinoAction) {
        this.yukinoAction = yukinoAction;
    }

    //执行处理器
    public String execute() {
        return yukinoAction.execute();
    }
}

这里解释一下为什么要用Process封装处理器action,而不直接调用action呢?这样做不是多此一举吗?其实这里还可以封装一些其他的对象,如处理器action的前置增强,后置增强等。方法也可以变成执行前置,执行后置,全部执行等多种,让处理器的使用更为灵活。

到此问题的原因显而易见了,TestProcess是通过工厂模式从springboot中获得的,采用的是默认的单例模式。因此在封装处理器(更改属性)时受到了其他线程的影响,导致在第二步执行的时候,用了错误的处理器,拿到了错误的结果,从而引起了ThreadLocal中储存的键和值出现了不匹配的问题。


解决方案:

@Component
public class ProcessBeanFactory {

    public static TestProcess getProcess(String s){
        //获得process对象(处理器action封装在次对象中)
        TestProcess process = new TestProcessImplA();
        //根据参数s获取用于具体执行的action
        YukinoAction action = YukinoContext.getBean(s, YukinoAction.class);
        //封装bean对象里用于执行的属性
        process.setAction(action);
        //返回
        return process;
    }

}

TestProcess不再使用单例模式即可。

因此在编写代码时应该小心单例模式的属性变化问题。

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值