今天重構公司的代碼,看到有一個單例的對象,在整個系統中到處都存在調用它的身影,因為我們這個項目會在應用服務器(server)第一次啟動的時候加載數據庫中的代碼表(為什么要先加載,因為這個項目采用的是C/S模式,利用remoting進行通信,在客戶端啟動的時候會多次獲取代碼表中的不同數據。)
1 public class CodeTableSingleTon 2 { 3 ///防止外部被實例化 4 private CodeTableSingleTon(){ 5 //進行數據的讀取操作 6 } 7 //鎖 8 private static readonly object _lock=new object(); 9 10 private static CodeTableSingleTon _instance; 11 12 public static CodeTableSingleTon Instance 13 { 14 get 15 { 16 if(_instance==null) 17 { 18 lock(_lock){ 19 if(_instance==null) 20 { 21 _instance=new CodeTableSingleTon(); 22 } 23 } 24 } 25 return _instance; 26 } 27 } 28 29 30 }
上面代碼就是項目的實現方式,我了解過單例模式,單例模式是控制對象的創建,可以實現延遲加載,只有在對象被調用的時候才會真正的執行對象的實例化,可以在一定程度上減少內存的耗用(是在不使用該對象的情況下)。既然在整個系統中只有一個該對象, 因為項目是采用多線程處理客戶端的請求,所以每個線程都可以獲取到該對象進行數據的獲取,系統運行也沒發現 由於這個對象的創建或者其他問題。
但是我深入的想了一下,如果我在單例對象中加入一個計數器,表示單例對象被調用的次數,那么這個對象內部的屬性 可能會同時被多個線程進行更新。
1 public class SingleTon 2 { 3 4 private static SingleTon _instance; 5 private static readonly object _lock=new object(); 6 private SingleTon(){} //私有的構造函數 確保該實例不能在外部直接通過new實例化 7 public static long totalNum=0;//初始化計數器默認值為0 8 public static SingleTon Instance 9 { 10 get 11 { 12 if(_instance==null) 13 { 14 lock(_lock) 15 { 16 if(_instance==null) 17 { 18 _instance=new SingleTon(); 19 } 20 } 21 } 22 totalNum=totalNum+1; //每次調用計數器都會加1
//Consle.WriteLine(totalNum); 23 return _instance; 24 } 25 } 26 }
我嘗試開啟了多個線程來執行這段代碼,沒有發現totalNum的值存在由於多個線程的調用出現重復數據輸出的情況。那么我心里一直在糾結一個事,就是說假如兩個線程都在同一時間獲取到了這個單例對象,那么都會依次執行計數器加1,如果說兩個線程執行到totalNum=totalNum+1 這句話的時候不是在同時的,那么不會出現問題。
如果這兩個線程中了彩票了,同時執行了totalNum=totalNum+1,那么就會導致計數器少算了一次調用嗎?我在測試,開啟了100個線程,沒有發現重復的值存在,難道我的理解是錯誤的嗎?我記得在多線程下操作同一數據是會出現這個問題的?不知道這個對單例模式是否適用呢?糾結糾結,求准確的回復
如果單例的方式存在問題,那么采用靜態字段也會出現這個問題,就無法統計計數器了。mygod 腦袋要炸了