项目需求:
产品需要监视不同种类的多个数据库,例如:多个mysql库,多个oracle库,多个sybase库,多个msserver库等等,连接池需要根据客户数据库种类和数量的实际情况进行动态创建。
难点:
1.每个库创建一个连接池,不能重复创建。
2.每个连接池维护自身的线程安全性,访问每个连接池的线程单独排队,相互之间不能影响。
实现思路:
将每个库的连接池实例保存到一个static Map中,key为库路径,value为连接池对象。如果对象已经存在,则不再创建,直接获取。
Map的read-write的组合操作,需要线程安全控制。创建连接池对象时,需要先初始化pool对象,这是一个耗时的操作,这时引申出一个问题,以什么作为锁来较好地控制对Map的操作?
如果是全局变量锁,则会导致不同的数据库都会在这里阻塞,如果一台数据库宕掉,其他数据库也会大量阻塞。如果用库地址作为独占锁,则可以实现不同的数据库各自排队,库和库之间没有影响。因为库路径是String对象,为此也专门测试了一下Java String对象是否可以做锁变量。结论是Java String对象不能作为Synchronized()的锁变量。但是Object对象可以作为锁,很奇怪!
不能在全局变量上加锁的主要原因是初始化连接池对象时间较长,线程排队会比较严重,而且多数据库间互相影响。所以考虑添加一个锁集合,以数据库路径为key,以Object对象为value,这样可以较快的获取Object对象,并且可以用这个对象代替key(数据库路径)作为synchrozied的锁变量。上代码:
import java.sql.Connection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import com.mchange.v2.c3p0.ComboPooledDataSource; public class XJDBCPool { public static enum DBTYPE { Oracle, Sybase, Mysql, Mssql, DB2, Informix }; //locks map public final static Map<String, Object> locks = new ConcurrentHashMap<String, Object>(); //pool map public final static Map<String, C3p0Pool> jdbcPoolC3p0s = new ConcurrentHashMap<String, C3p0Pool>(); private static XConnection getXCon(String key, String type) { Logger.logDebug("~~~~ XJDBCPool getXCon key : "+ key); int timeoutIndex = key.lastIndexOf("#"); String timeStr = key.substring(timeoutIndex + 1) == null ? "" : key.substring(timeoutIndex + 1); int timeout = Integer.valueOf(timeStr); key = key.substring(0, timeoutIndex); C3p0Pool c3p0Pool = null; if(type.equals(JDBC_TYPE_C3P0)){ Object obj = null; synchronized(locks){ obj = locks.get(key); if(obj == null){ obj = new Object(); locks.put(key, obj); } } Logger.logDebug("~~~~ ["+ Thread.currentThread().getName() +"] wait lock : "+ obj +" key : "+ key); synchronized(obj){ Logger.logDebug("~~~~ ["+ Thread.currentThread().getName() +"] get lock : "+ obj +" key : "+ key); c3p0Pool = jdbcPoolC3p0s.get(key); if (c3p0Pool == null){ //init pool c3p0Pool = new C3p0Pool(key, timeout); jdbcPoolC3p0s.put(key, c3p0Pool); } else{ //if timeout was modified, restart pool. int timeoutOld = 0; ComboPooledDataSource cpds = c3p0Pool.getCpds(); if(cpds != null){ timeoutOld = cpds.getCheckoutTimeout(); if(timeout * 1000 != timeoutOld){ c3p0Pool.release(); cpds = c3p0Pool.init(key, timeout); jdbcPoolC3p0s.put(key, c3p0Pool); } } } } Logger.logDebug("~~~~ ["+ Thread.currentThread().getName() +"] release lock : "+ obj +" key : "+ key); } return c3p0Pool.getConnection(); } .... }
“String对象不能作为synchrozied的锁变量”
测试程序如下,控制台输出大家相信都能看得懂,就不再做解释。
import java.util.Map; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import org.apache.log4j.Logger; public class TestSynchronizedStr { public static Logger logger = Logger.getLogger(LoggerManager.PLATFORM); public final static Map<String, Object> locks = new ConcurrentHashMap<String, Object>(); private final static Map<String, Object> jdbcPoolC3p0s = new ConcurrentHashMap<String, Object>(); /** * test ConcurrentHashMap */ public static void testConcurrentMap(String key){ Object obj = null; synchronized(locks){ obj = locks.get(key); if(obj == null){ obj = new Object(); locks.put(key, obj); } } logger.debug("~~~~ ["+ Thread.currentThread().getName() +"] wait lock : "+ key); synchronized(obj){ logger.debug("~~~~ ["+ Thread.currentThread().getName() +"] get lock : "+ key); try{ Thread.sleep(10000L); } catch(Exception e){ e.printStackTrace(); } logger.debug("~~~~ ["+ Thread.currentThread().getName() +"] release lock : "+ key); } } public static void startThreads(final String i) { Thread[] threads = new Thread[10]; for(int j = 0; j < threads.length; j++){ threads[j] = new Thread(){ public void run(){ logger.debug("\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); logger.debug("**** start ["+ Thread.currentThread().getName() +"]"); testConcurrentMap(i); } }; threads[j].start(); } } public static void main(String[] args) { for (int i = 0; i < 50; i++) { Random random = new Random(); int randNum = Math.abs(random.nextInt())%3; startThreads(randNum +""); } } }