项目需求:
产品需要监视不同种类的多个数据库,例如:多个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 +"");
}
}
}