DBUtil數據庫連接單例 —— 簡單不簡單


單例大概是我最早產生明確模式意識的設計模式,因為它足夠簡單粗暴,目的足夠明確。

單例么,就是不管怎么訪問,都返回一個單一實例就好了,我最早應用在數據庫的DBUtil中。

 

 1 public class DBUtils
 2 {
 3     
 4     //驅動串
 5     private final static String Driver="com.mysql.jdbc.Driver";
 6     //連接串
 7     private static String url="jdbc:mysql://"+ SysProperties.MYSQL_HOST+":"+SysProperties.MYSQL_PORT+"/"+SysProperties.MYSQL_NAME+"?autoReconnect=true&autoReconnectForPools=true";
 8     //Connection
 9     private static Connection conn;
10     
11    
12     
13     private DBUtils()
14     {
15     }
16     
17 
18     
19     public static Connection getConnection()throws Exception
20     {
21         url="jdbc:mysql://"+SysProperties.MYSQL_HOST+":"+SysProperties.MYSQL_PORT+"/"+SysProperties.MYSQL_NAME;
22         CE.lgInfo("url:" + url);
23         
24         if(conn==null || conn.isClosed())
25         {
26             conn=DriverManager.getConnection(url, SysProperties.MYSQL_USER, SysProperties.MYSQL_PWD);
27         }
28         
29         return conn;
30     }
31 
32 }

 

很完美,在我的小程序里跑了近兩年。

 

可是有的時候檢查訂單,就會發現每個階段的訂單都有那么幾塊錢無論如何對不上。這一直是個幽靈bug,困擾許久。

知道有一天,檢查日志的的時候發現,當一個數據庫在訪問數據庫進行鎖表的時候,被鎖內容卻被另一個操作修改掉了。於是,我第一次看到了線程的影子。

但是那時候我對線程的理解完全不夠透徹,只是模糊知道:

  1. 可能是有好幾個請求同時訪問了這個方法;
  2. 在一個請求要new Connection的時候,將new未new之際,另一個請求插隊了,也擠進來了;
  3. 第二個請求一看,哦,沒有connection,我也new一個,其實此時第一個請求已經在new了,只不過還沒new出來而已;
  4. 結果就出來了兩個Connection,單例?呵呵……

然后知道了有個關鍵字叫synchronized, 好,改。

 1     public static synchronized  Connection getConnection()throws Exception
 2     {
 3         url="jdbc:mysql://"+SysProperties.MYSQL_HOST+":"+SysProperties.MYSQL_PORT+"/"+SysProperties.MYSQL_NAME;
 4         CE.lgInfo("url:" + url);
 5         
 6         if(conn==null || conn.isClosed())
 7         {
 8             conn=DriverManager.getConnection(url, SysProperties.MYSQL_USER, SysProperties.MYSQL_PWD);
 9         }
10         
11         return conn;
12     }

於是變成了這樣。於是,同一時間只有一個線程可以訪問這個方法,其他人想訪問,好,你等着吧,等我搞完你再上。

所有對該方法的請求被JVM強制排起了隊,自然不會被new出來多個Connection了。

跑了半年,似乎不錯。

 

但是后來忽然想,我之所以使用synchronized,是不想讓他重復new,而不是不想讓他重復訪問。感覺就像什么呢,大家去生孩子,為了讓醫生好好工作,讓大家都要排隊進產房,這很正常,可是我現在除了讓大家排隊進產房,還讓大家排隊懷孕……這就扯淡了。

於是查資料,知道了synchronized不僅可以作用於方法,還可作用於區塊。於是有了下面這個:

 1     public static synchronized  Connection getConnection()throws Exception
 2     {
 3         url="jdbc:mysql://"+SysProperties.MYSQL_HOST+":"+SysProperties.MYSQL_PORT+"/"+SysProperties.MYSQL_NAME;
 4         CE.lgInfo("url:" + url);
 5         
 6         if(conn==null || conn.isClosed())
 7         {
 8             synchronized (DBUtil.class){
 9                 if(conn==null || conn.isClosed()){
10                     conn=DriverManager.getConnection(url, SysProperties.MYSQL_USER, SysProperties.MYSQL_PWD);
11                 }
12             }
13         }
14         
15         return conn;
16     }

只有為null的時候,才排隊。為啥判斷兩次?更安全啊。

當然,后來知道了他有個更高大上的名字,稱為“雙重檢查鎖”。

 

所以,一個單例,真的簡單么?可以用最快速的方法實現一個單例,也可以用相對更安全的方法實現一個單例。

如果你在面試,一個單例寫下來,就知道你是一個菜鳥還是老手了。

 


免責聲明!

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



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