springmvc+hibernate+jdbctemplate+mysql 原文鏈接:http://blog.csdn.net/rugaxm/article/details/8551905 先看下麵小段代碼,一個controller,一個service。 controller.java代碼: .. ...
springmvc+hibernate+jdbctemplate+mysql
原文鏈接:http://blog.csdn.net/rugaxm/article/details/8551905
先看下麵小段代碼,一個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正確時,可以確定不會出現併發問題。