spring全局變量引起的並發問題


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

       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;
        //業務邏輯處理
    }

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

    編碼者這樣寫的目的是因為content等變量需要在多個方法中使用,而且變量很多,但又不想通過方法參數的方式來傳遞,故使用成員變量。

    先看看為什么會出現這種情況。 由於系統采用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正確時,可以確定不會出現並發問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM