zoukankan      html  css  js  c++  java
  • 连接池初涉——自定义连接池

    关于自定义连接池引入:

          使用JDBC进行数据库连接的时候,我们每次一个insert操作等等之后,就会把Connection关闭掉;但是这样是很低效的,我们的Connection在一次使用之后,就面临被关闭,加载和关闭太浪费资源了,而且这样放任外部自由的修改,不加限制的创建Connection对象,有可以会超过数据库能够承受的最大连接数,数据库容易崩溃!
         
          我们的解决此类问题的办法:
                创建一个连接池,外部要和数据库进行连接,必须通过连接池进行连接,连接池一次初始会初始定量的连接对象,也会限制程序能够 操作的最大的连接数量。如果超过了此最大连接数,就会抛出异常或者让用户进行等待。当用户不使用连接之后,就将连接放回连接池中。
         
         
    关于静态代理和动态代理的引入:
          我们发现,在程序使用完了从连接池中获得的连接对象之后,必须要使用我们连接池内部提供的release方法进行连接的释放(即将其放回连接 池中),但是如果外部程序使用完了连接之后,直接close()了怎么办呢?
         
          试想外部程序直接close了之后会发生的结果:
          我们先构建一个场景:我们的初始化的连接数量为5,最大连接数量为10;并且我们当前正处于JDBC操作的峰值上,还有很多等待JDBC操作的程序;
          如果外部程序使用完了我们的连接对象,直接close的话,就会造成我们 当前的连接对象不能放回连接池中,但是连接池记录的当前的连接对象的数量依旧是10,但是我们当前已经close了一个连接,所以实际池中只有9个连接对象了;如果此类情况一直发生,就会造成我们连接池中的连接数量最后为0,但是连接池自身记录的连接数一直处于峰值状况,所有的 连接都是处于不可用的状态!那么这个连接池就算是废了!
         
    首先引入的是静态代理:
          关于代理之前,我们要先复习一个概念;
          在JDBC中,JDBC所有的实现类都是数据库提供商编写的,java只提供了 一个规范,一堆接口;数据库提供商面向接口编程的;那么在我们使用JDBC操作的时候,我们开发者也是通过java.sql.*中的接口,作为引用操作的各个数据库提供商写出的实现类的对象;
          代理的概念就是:以前我们是直接使用Connection接口,现在:我们在Connection接口之上,再添加一个我们的Connection操作类,implements  Connection,然后,将其中我们需要修改的方法进行重写,其它的进行原样调用。
         
         
          静态代理就是完全重写一个接口,将其中所有的方法都要修改,不修改的就必须通过手动修改的方式和下层进行相连,当接口中方法很多的时候,这样很低效;
         

          动态代理:首先要运用反射中的Proxy类,来创建动态代理对象;通过反射的操作来降低我们代码中的重复操作(或低效代码)。

    package pool;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.util.LinkedList;
    /**
     * 自定义连接池
     * @author mzy
     */
    public class MyPool {
    	private static String url="jdbc:mysql://localhost:3306/test";
    	private static String user="root";
    	private static String password="123456";
    	private static String driverClass="com.mysql.jdbc.Driver";
    	
    	private static LinkedList<Connection> pool = new LinkedList<Connection>();
    	// 连接池的初始化连接数
    	private int initCount = 5;
    	// 连接池的最大连接数
    	private int maxCount = 10;
    	// 用于记录当前连接的数量
    	private int currentCount = 0;
    	
    	public static LinkedList<Connection> getPool() {
    		return pool;
    	}
    	
    	public static void setPool(LinkedList<Connection> pool) {
    		MyPool.pool = pool;
    	}
    
    	static {
    		// 注册驱动
    		try {
    			Class.forName(driverClass);
    		} catch(ClassNotFoundException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	public MyPool() {
    		for(int i=1; i<=initCount; i++) {
    			pool.addLast(createConnection());
    			currentCount++;
    		}
    	}
    
    	private Connection createConnection() {
    		final Connection conn;
    		try {
    			conn = DriverManager.getConnection(url, user, password);
    			// 1) 使用静态代理类的方式去创建Connection的代理类
    			// MyConnection myConn = new MyConnection(this, conn);
    			
    			// 2) 使用动态代理类方式去创建Connection的代理类
    			/**
    			 *  使用到jdk的api:  Proxy类
    			 *  		用于创建动态代理类对象:
    			 *  		static Object newProxyInstance(
    			 *  						ClassLoader loader,
    			 *   						Class<?>[] interfaces, 
    			 *   						InvocationHandler h
    			 *   						)  
    			 * 			参数一:类加载器。
    			 * 			参数二: 代理类实现的接口列表
    			 * 			参数三: 接口 InvocationHandler: 代理类的调用处理程序的接口。(代理完代理对象之后,对其中的方法如何处理???)
    			 * 					Object invoke(
    			 * 						Object proxy,  代理类对象      
    			 * 						Method method,  代理类对象调用的方法。 
    			 * 						Object[] args  调用代理类对象方法时传入的参数列表
    			 * 					)    
    			 */
    			Connection myConn = (Connection)Proxy.newProxyInstance(MyPool.class.getClassLoader(), 
    					new Class[]{Connection.class}, 
    					new InvocationHandler() {
    
    						@Override
    						public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    							// 1) 重写需要重写的方法。close方法
    							// 获取当前调用的方法的方法名称
    							String methodName = method.getName();
    							if("close".equals(methodName)) {
    								MyPool.getPool().addLast(conn);
    								return null;
    							} else {
    								// 2) 调用回原来的方法,获取返回值
    								Object value = method.invoke(conn, args);
    								return value;
    							}
    						}
    				
    			});
    			return myConn;
    		} catch (SQLException e) {
    			e.printStackTrace();
    			throw new RuntimeException(e);
    		}	
    	}
    	
    	/**
    	 * 对外提供给java程序一个获取连接的方法
    	 */
    	public Connection getConnection() {
    		// 1) 当并发连接数小于等于初始化连接数量的时候,才从池中取出
    		if(pool.size()>0) {
    			System.out.println("==== 初始化的连接  ==== ");
    			return pool.removeFirst(); // 取出并删除
    		}
    		
    		// 2) 当并发数量超过初始化连接数据的时候,程序自行获取连接对象,但是
    		// 一旦超过了最大连接数量的时候,不能获取
    		if(currentCount<maxCount) {
    			System.out.println("==== 新建的连接  ==== ");
    			currentCount++;
    			return createConnection();
    		}
    		
    		// 3) 当超过了最大连接数时,不能再获取连接了。
    		throw new RuntimeException("已经超过了最大连接数");
    	}
    	
    	/**
    	 * 对外提供释放连接对象的方法
    	 */
    	public void releaseConnection(Connection conn) {
    		// 放回连接池容器中
    		pool.addLast(conn);
    	}
    }
    

     

      测试方法:

    package pool;
    
    import java.sql.Connection;
    
    public class TestPool {
    	public static void main(String[] args) {
    		// 1) 构造连接池对象
    		MyPool myPool = new MyPool(); // 初始化连接
    		// 模拟用户并发获取连接
    		for(int i=0; i<11; i++) {
    			Connection conn = myPool.getConnection();
    			// 如果获取的连接数小于初始化连接数,就不用真的连接数据库
    			System.out.println("第"+(i+1)+"个"+conn);
    			
    			if(i == 3) {
    				// 模拟用户释放连接,把连接放回连接池中
    				myPool.releaseConnection(conn);
    			}
    		}
    		
    		/*
    		 * 我们当前的自定义pool已经完成了;
    		 * 但是仍然有问题;
    		 * 我们当前的连接池是没有加锁的,如果多个程序同时来拿的话,
    		 * 是线程不安全的;
    		 * 其次,当连接数量达到max的时候,当再有程序想获得Connection
    		 * 我们直接是抛出一个runtimeException;
    		 * 不合理,让用户进行等待sleep才是一个合理的操作!
    		 */
    	}
    	/**
    	 * 所以我们通常使用别人写好的连接池工具:
    	 * 	通常使用DBCP(DataBase Connection Pool)
    	 * 		是Apache组织编写的产品
    	 * 
    	 * C3P0
    	 * 		是开源框架使用的(hibernate内置默认的连接池工具C3P0)
    	 */
    }
    
  • 相关阅读:
    作业要求 20181030-1 Alpha发布用户使用报告
    Scrum立会报告+燃尽图(Beta阶段第二周第二次)
    Scrum立会报告+燃尽图(Beta阶段第二周第一次)
    20181113-3 Beta阶段贡献分配规则
    20181113-1 版本控制报告
    Scrum立会报告+燃尽图(Beta阶段第七次)
    Scrum立会报告+燃尽图(Beta阶段第六次)
    Scrum立会报告+燃尽图(Beta阶段第五次)
    Scrum立会报告+燃尽图(Beta阶段第四次)
    Scrum立会报告+燃尽图(Beta阶段第三次)
  • 原文地址:https://www.cnblogs.com/mzywucai/p/11053471.html
Copyright © 2011-2022 走看看