項目問題總結1、全局變量引發的併發問題

最近剛完成了一個短彩信羣發平臺的開發工做,系統採用springmvc+hibernate+jdbctemplate+mysql架構。其中碰見許多問題,閒暇之餘記錄下來以免在後續項目中再犯一樣的錯誤。java

先看下面小段代碼,一個controller,一個service。mysql

       controller.java代碼:
    ........
    @Autowired
     private XXXService xxxService;
    ........
    @RequestMapping("/doXXX.do")
    public void doXXX(){
        .....
        xxxService.saveXXX(String content,....);
        .....
    }
    XXXService.java代碼:
    private String content;
    ......
    private void init(){//清空請求參數
        content = null;
        ......
    }
    public boolean saveXXX(String content, ......){
        this.init(content, ...);
        this.content = content;
        //業務邏輯處理
    }web

    以上這段代碼在訪問量不構成併發時不會出現什麼問題。 但當一個請求還未完成,另外一個請求已經開始執行的狀況下就會出現問題(併發): 第二個請求執行執行init()方法會將第一個請求的content變量設置爲null或它自己的值,這樣數據就被篡改了。spring

    編碼者這樣寫的目的是由於content等變量須要在多個方法中使用,並且變量不少,但又不想經過方法參數的方式來傳遞,故使用成員變量。sql

    先看看爲何會出現這種狀況。 因爲系統採用springmvc框架,springmvc核心控制器DispatcherServlet 默認爲每一個controller生成單一實例來處理全部用戶請求,因此在這個單一實例的controller中,它的XXXService也是一個實例處理全部請求, 這樣XXXService的成員變量就被全部請求共享。這樣就會出現併發請求時變量內容被篡改的問題。 編程

    那麼出現這種問題如何解決呢?
    第一種方式: 既然是全局變量惹的禍,那就將全局變量都編程局部變量,經過方法參數來傳遞。
    第二種方式: jdk提供了java.lang.ThreadLocal,它爲多線程併發提供了新思路。 (當使用ThreadLocal維護變量時,ThreadLocal爲每一個使用該變量的線程提供獨立的變量副本,因此每個線程均可以獨立地改變本身的副本,而不會影響其它線程所對應的副本
         那麼在什麼地方使用ThreadLocal呢? 什麼變量是請求公用的就將該變量託付給ThreadLocal來管理其線程副本, 因此咱們在xxxService中使用它。
        XXXService.java代碼:
        private ThreadLocal<String> contentTL = new ThreadLocal<String>();
        //private String content;使用contentTL代替content;
        ......
        public boolean saveXXX(String content, ......){
            this.contentTL.set(content);  多線程

            //業務邏輯處理
            //在各方法中使用content時候用this.contentTL.get()代替
    }  架構

     此類併發篡改數據的問題,能夠在開發工具中設置斷點調試的方式來模擬併發。即第一次請求運行到斷點時,查看content內容,而且不讓程序繼續往下運行,同時再發起一個請求,查看content內容。 如內容是第一次請求的內容,而且讓第一個請求跑完後,第二個請求到斷線處的content正確時,能夠肯定不會出現併發問題。併發

相關文章
相關標籤/搜索