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