实践缩小Java synchronized 粒度

项目需求:

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

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值