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);
    }
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章