記一次抽象類中定義的靜態變量,多個子類繼承後,在方法中被重寫引發的問題

N年沒有寫過博客了……多線程

開始:app

2018.08.03  搬家項目,版本昨晚剛上線,今早測試與供應商的估價接口,發現問題。ide

背景:post

    我司對接三家供應商A、B、C,各家的Url,appid不一樣,分別配置在配置文件中。測試

 

抽象類(截取部分片斷):lua

 

 1 public abstract class AbstractSupplierOrderService implements ISupplierOrderService {
 2 
 3 
 4     protected static ISupplierConfig supplierConfig;
 5 
 6     @Autowired
 7     private MovTaskMapper movTaskMapper;
 8 
 9     //根據供應商code 獲取對應供應商服務實例
10     public static ISupplierConfig getSupplierConfig(String supplierCode) {
11         if (SupplierEnum.LANXINIU.getCode().equals(supplierCode)) {
12             return SpringContextUtil.getBean(SupplierConfigLanxiniu.class);
13         }
14         if (SupplierEnum.ZIROOM.getCode().equals(supplierCode)) {
15             return SpringContextUtil.getBean(SupplierConfigZiroom.class);
16         }
17         if (SupplierEnum.SITONG.getCode().equals(supplierCode)) {
18             return SpringContextUtil.getBean(SupplierConfigSitong.class);
19         }
20         return null;
21     }
22 
23     @Override
24     public ResultInfoVo quotedPrice(EvaluateOrderPriceVo orderVo) {
25         supplierConfig = AbstractSupplierOrderService.getSupplierConfig(orderVo.getSupplierCode());
26         String quotedPriceUrl = supplierConfig.getQuotedPriceUrl();
27 
28         ResultInfoVo resultInfoVo = null;
29         EvaluateOrderPriceVo evaluateOrderPriceVo = null;
30 
31         try {
32             Map voMap = ObjectToMapUtils.objectToMapString(null, orderVo, "");
33             Map paramMap = getSupplierService(orderVo.getSupplierCode()).getOrderParams(voMap);
34 
35             log.info("=============調用供應商獲取報價信息接口入參:{}=============", JSONObject.toJSONString(paramMap));
36             resultInfoVo = postSupplierServices(orderVo.getSupplierCode(), evaluateRestTemplate, quotedPriceUrl, paramMap);
37         } catch (Exception e) {
38             log.error("=============調用供應商獲取報價信息異常,入參:{}=============", JSONObject.toJSONString(orderVo), e);
39             throw new OrderServiceException("調用供應商獲取報價信息異常", e);
40         }
41 
42         log.info("=============調用供應商獲取報價信息接口出參:{}=============", JSONObject.toJSONString(resultInfoVo));
43 
44         return resultInfoVo;
45     }
46 
47 }
View Code

 

調用入口:url

 1 public ResultInfoVo evaluateOrderPrice(EvaluateOrderPriceVo evaluateOrderPriceVo) {
 2        ===========省略==========
 3         log.info("========估價流程開始,供應商code:{}========", evaluateOrderPriceVo.getSupplierCode());
 4         //動態獲取服務商實例
 5         ISupplierOrderService supplierOrderService = AbstractSupplierOrderService.getSupplierService(evaluateOrderPriceVo.getSupplierCode());
 6         //調用供應商獲取報價接口
 7         try {
 8             quotedPriceResult = supplierOrderService.quotedPrice(evaluateOrderPriceVo);
 9         } catch (Exception e) {
10         }
11         ===========省略==========
12     }

 

 

抽象類中有靜態變量:supplierConfig。而 靜態變量位於抽象類類對象的方法區,三個實現子類共用該靜態變量。 如各子類對該靜態變量賦值需求不一樣,在多線程狀況下,會出現問題。spa

排查問題時的日誌:線程

 

日誌中能夠看到,問題出在線程2,8。 3d

 

分析:

  線程二、一、8前後進入估價流程MovOrderService.evaluateOrderPrice,

一、線程8先根據對應的1003的supplierConfig實現,組裝參數,生成sign。 實現類中取的 supplierConfig是在實現類中的一個類變量,因此不會共用抽象類中的類變量,取得apppid是對的

     

二、線程2根據抽象類中的靜態類變量supplierConfig獲取url。但此時supplierConfig被線程2更新成了線程2對應的實例,取的是線程2的對應配置。因此url取錯了。

 

總結:多線程環境下,對於可變的變量,在抽象基類中,慎用靜態變量。

由於各實現類都引用了基類的靜態類變量(即使子類中本身定義了同名變量,也與基類中的變量不是一個存儲空間)。該基類靜態變量只有這麼一個存儲空間。 因此會很容易被修改,致使意想不到的結果。

相關文章
相關標籤/搜索