JAVA设计模式大总结系列博客(六)节约性能的对象池模式

定义

对象的实例化是最耗费性能的操作之一,这在过去是个大问题,现在不用再过分关注它。但当我们处理封装外部资源的对象(例如数据库连接)时,对象的创建操作则会耗费很多资源。解决方案是重用和共享这些创建成本高昂的对象,这称为对象池模式。

这其实就是说,有些资源我可能获取起来不容易,如果频繁的获取就会特别耗费资源,因此,我获取完后想尽可能的重用他们,以减少我获取的次数,从而节约资源和提升性能,由此,引入了对象池这一说。

写到这里,越来越发现,其实这些所谓的设计模式,我们在平时就已经使用过了,比如这里的模式其实就是我们常用的数据库连接池这个工具,只是使用的时候还不知道这就是一种设计模式,到现在我们才知道,哦,原来我用的这个东西背后有这么一个模式啊。

组成

  • ResourcePool(资源池类):用于封装逻辑的类。用来保存和管理资源列表。
  • Resource(资源类):用于封装特定资源的类。资源类通常被资源池类引用,因此只要资源池不重新分配,它们就永远不会被回收
  • Client(客户端类):使用资源的类。

流程

  • 当Client 需要资源的时候,会向ResourcePool 索要一个可用资源,这些资源就是Resource
  • Client 使用资源完成后释放这个资源到ResourcePool
  • 释放后的这个资源又可以给别的应用调用。

Redis连接池示例

为了方便说明,我这里用 Redis 的连接池来说明一下。

  • Redis 连接池的代码如下:
public class JedisClient {

	public static JedisPool jedisPool = null;

	/**
	 * 初始化Redis连接池
	 */
	 static {
		
		if(jedisPool == null){
			JedisPoolConfig config = new JedisPoolConfig();
			/*// 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
			config.setBlockWhenExhausted(true);
			// 设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)
			config.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy");
			// 是否启用pool的jmx管理功能, 默认true
			config.setJmxEnabled(true);
			// 最大空闲连接数, 默认8个 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。
			config.setMaxIdle(8);
			config.setMaxTotal(100);
			// 表示当borrow(引入)一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
			config.setMaxWaitMillis(100000);
			config.setTestOnReturn(true);*/
			config.setMaxTotal(200);
			config.setMaxIdle(50);
			config.setMinIdle(8);//设置最小空闲数
			config.setMaxWaitMillis(10000);
			config.setTestOnBorrow(true);
			config.setTestOnReturn(true);
			//Idle时进行连接扫描
			config.setTestWhileIdle(true);
			//表示idle object evitor两次扫描之间要sleep的毫秒数
			config.setTimeBetweenEvictionRunsMillis(30000);
			//表示idle object evitor每次扫描的最多的对象数
			config.setNumTestsPerEvictionRun(10);
			//表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义
			config.setMinEvictableIdleTimeMillis(60000);
			String serverAddress = Play.configuration.getProperty("redis.server.address");
			String password = Play.configuration.getProperty("redis.server.password");
			int serverPort = Integer.valueOf(Play.configuration.getProperty("redis.server.port"));
			int serverDatabase = Integer.valueOf(Play.configuration.getProperty("redis.server.database"));
			jedisPool = new JedisPool(config, serverAddress, serverPort, 10000, password,serverDatabase);
		}
	}
  • 使用连接池的时候
/**
	 * 获取Jedis实例
	 * @return
	 */
	public synchronized static Jedis getJedis() {
		try{
			if (jedisPool != null) {
				Jedis resource = jedisPool.getResource();
				return resource;
			} 
		}catch(Exception e){
			e.printStackTrace();
		}
		return null;
	}
  • 释放连接池的时候
/**
	 * 释放jedis资源
	 * @param jedis
	 */
	public static void close(final Jedis jedis) {
        if (jedis != null) {
    	    jedis.close();
    	    
        }
	}

总结

对象池模式是一个运用特别广泛的模式。几乎所有的中间件都可以用这个 模式进行资源的调用和释放,还有许多大型数据库都是支持连接池模式的。各位小伙伴,你们平时是怎么使用它的?

附:传送门

  • Mysql数据库连接池运行思路
    服务器启动时会建立一定数量的连接,并一直维持不少于这个数目的连接。客户端需要连接的时候,连接池驱动会返回一个没使用的连接池连接并把它标记为忙。如果没有空闲的连接,连接驱动会根据你的连接池配置重新建立一些连接。当使用完这个连接时,连接驱动会将这个连接重新标志为可用。现方式,返回的 Connection是原始Connection的代理,代理Connection的close方法不是真正关连接,而是把它代理的 Connection 对象还回到连接池中。
  • 数据库连接池代码示例
  public void demo(){
 
    // 获得连接:
    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try{
      // 创建连接池:
      ComboPooledDataSource dataSource = new ComboPooledDataSource();
      // 设置连接池的参数:
      dataSource.setDriverClass("com.mysql.jdbc.Driver");
      dataSource.setJdbcUrl("jdbc:mysql://test");
      dataSource.setUser("root");
      dataSource.setPassword("1234");
      dataSource.setMaxPoolSize(20);
      dataSource.setInitialPoolSize(3);
       
      // 获得连接:
      conn = dataSource.getConnection();
      // 编写Sql:
      String sql = "select * from user";
      // 预编译SQL:
      pstmt = conn.prepareStatement(sql);
      // 设置参数
      // 执行SQL:
      rs = pstmt.executeQuery();
      while(rs.next()){
        System.out.println(rs.getInt("uid")+"  "+rs.getString("username")+"  "+rs.getString("password")+"  "+rs.getString("name"));
      }
    }catch(Exception e){
      e.printStackTrace();
    }finally{
      JDBCUtils.release(rs, pstmt, conn);
    }
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章