`

实践缩小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
0
分享到:
评论
2 楼 liudajiang 2014-10-16  
呵呵
            startThreads(randNum +""); 
每次传进去的都是不同的string对象(+操作被编译为stringbuiler.append(str).tostring()),当然锁不住,用1楼的intern()返回常量引用就解决了
1 楼 laser_mesty 2014-07-04  
String 对象是可以做为多变量的。例子显示 threand1-10获取所变量s=1,thread11-20 获取String变量 s1=1时,各自获取各自锁变量,当String.intern()之后,thread1-20获取相同的锁变量 值1

相关推荐

    实例解析Java中的synchronized关键字与线程平安问题_.docx

    我们在使用 synchronized 关键字的时候,能缩小代码段的范围就尽量缩小,能在代码段上加同步就不要再整个方法上加同步。这叫减小锁的粒度,使代码更大程度的并发。 synchronized 方法被修饰的方法称为同步方法,其...

    实例解析Java中的synchronized关键字与线程安全问题

    synchronized代码块,被修饰的代码成为同步语句块,其作用的范围是调用这个代码块的对象,我们在用synchronized关键字的时候,能缩小代码段的范围就尽量缩小,能在代码段上加同步就不要再整个方法上加同步。...

    Java_Thread应该注意的问题

    - 尽量减小同步的范围,即缩小`synchronized`代码块。过度使用`synchronized`方法可能导致不必要的阻塞,降低性能。优先考虑使用`synchronized`块,只锁定必要的代码部分。如果必须使用方法级别同步,考虑使用`Lock...

    Java锁机制详解.pdf

    Java 6通过改进内部锁的管理算法,使得内置锁的性能有了显著提升,这在一定程度上缩小了与ReentrantLock之间的性能差异。 读-写锁(ReadWriteLock)是另一种显示锁机制,适用于读操作远多于写操作的场景。读-写锁...

    并发编程中是如何降低锁粒度的,怎么做到性能优化!.docx

    Java中,内存模型规定了线程间如何共享和访问变量,而同步机制如`synchronized`关键字就是用来保证内存可见性和原子性。但是,过度使用同步可能导致更多的内存同步开销。 再者,线程阻塞是指一个线程因为无法获取锁...

    java concurrency in practice.rar

    - **Lock接口与ReentrantLock**:提供了比synchronized更细粒度的锁控制,支持公平锁和非公平锁。 - **java.util.concurrent.atomic包**:提供原子操作类,如AtomicInteger、AtomicLong等,用于无锁编程。 3. **...

    JavaLock与Condition的理解Reentran

    Java Lock与Condition是Java并发编程中的重要概念,它们提供了比synchronized关键字更细粒度的控制,从而使得多线程程序的设计和管理更加灵活高效。本文将深入探讨ReentrantLock(可重入锁)和Condition的基本原理、...

    java代码优化总结1.0版本.7z

    使用synchronized关键字时,尽量缩小锁的粒度,考虑使用Lock接口提供的高级同步机制,如ReentrantLock。另外,合理利用并发容器,如ConcurrentHashMap,它们内部设计有优化的并发控制,比简单的同步集合更高效。 除...

    如何优化JAVA程序设计和编码,提高JAVA性能

    尽量缩小锁的粒度,使用`Lock`接口(如`ReentrantLock`)提供更细粒度的控制。如果可能,使用并发集合类(如`ConcurrentHashMap`)代替同步的`HashMap`。 5. **使用并发编程策略**:Java的并发库提供了很多高效工具...

    java 同步方法

    同步方法通过`synchronized`关键字实现,确保同一时间只有一个线程能够执行特定的方法或代码块,从而避免数据不一致性和竞态条件。然而,过度或不当使用同步方法可能导致性能问题,尤其是当多个线程频繁争用同一锁时...

    Java中瘦锁的应用

    瘦锁技术在Java虚拟机(JVM)中实现的基础是细粒度锁(Fine-Grained Locking),它允许对共享资源进行更精确的锁定。通过缩小锁的范围,即只锁定真正需要同步的代码块,可以减少不必要的锁争用和等待时间。这种技术...

    藏经阁-阿里巴巴Java开发手册PDF1.1.0版(非最新版).pdf

    6. **并发处理**:提倡使用并发容器如`ConcurrentHashMap`替代普通`HashMap`在多线程环境下的使用,避免使用`synchronized`修饰整个方法,而应尽量缩小锁的粒度,使用`synchronized`块或`Lock`。 7. **控制语句**:...

    提高Java程序性能

    7. **谨慎使用synchronized**:同步可能导致性能下降和死锁风险,只在必要时使用,并尽量缩小同步范围。理解同步的粒度,方法同步和代码块同步的差异。 8. **字符串连接首选StringBuilder或StringBuffer**:在进行...

    改善Java中锁的性能_.docx

    另外,`java.util.concurrent`包提供了许多并发数据结构和工具类,如`ReentrantLock`、`Semaphore`等,它们允许更细粒度的控制和优化。 另一种优化策略是使用分别锁(也称为分段锁),将大型数据结构分割成小块,每...

    Java并发编程实战

    13.4 在synchronized和ReentrantLock之间进行选择234 13.5 读-写锁235 第14章 构建自定义的同步工具238 14.1 状态依赖性的管理238 14.1.1 示例:将前提条件的失败传递给调用者240 14.1.2 示例:通过轮询与休眠...

    Java 高并发九:锁的优化和注意事项详解

    例如,将同步块尽可能缩小,仅在必要的代码段使用同步关键字,避免无谓的等待。 ```java // 优化前 public synchronized void syncMethod() { othercode1(); mutextMethod(); othercode2(); } // 优化后 public...

    多线程设计要点讲解 多线程设计要点讲解

    17. **细粒度同步**:通过缩小`synchronized`块的范围,减少锁的竞争,提高效率。 18. **线程间的通信**:可以使用wait/notify机制,wait()需配合notify()或notifyAll()使用,wait(毫秒数)可以在指定时间后结束等待...

    java 中同步方法和同步代码块的区别详解

    - **锁的对象**:同步方法默认使用当前对象作为锁,而同步代码块可以指定任意对象为锁,这允许更细粒度的控制,可以避免因一个线程阻塞而导致其他不相关的线程也被阻塞的情况。 - **可读性**:同步方法的语法简洁,...

Global site tag (gtag.js) - Google Analytics