對象池化工具commons-pool

  關於對象池的概念這裏不做過多解釋,除了用來緩存數據庫連接java.sql.Connection這種重量級對象也能用於其他場景。初始化對象的代價高,就值得池化。池化的一個代價是被重用的對象會在堆中停留很長時間。如果有大量對象存在於堆中,那用來創建新對象的空間就少了,因爲GC 操作會更爲頻繁。
  以SimpleDateFormat 爲例,這是 Java 中常用的一個類,用於解析和格式化日期字符串。
  但是 SimpleDateFormat 在多線程環境中並不是線程安全的。詳見這位仁兄的 SimpleDateFormat 的線程安全問題與 ThreadLocal

  實現線程安全的一種思路是用ThreadLocal進行安全變量的副本。

 static ThreadLocal<SimpleDateFormat> format1 = new ThreadLocal<SimpleDateFormat>() {
    @Override
    protected SimpleDateFormat initialValue() {
        return new SimpleDateFormat("yyyy-MM-dd");
    }
};

public String formatDate(Date date) {
    return format1.get().format(date);
}

  但是維持變量副本的資源也是需要消耗資源的。儘管ThreadLocal提供了remove方法以便在請求結束後釋放副本,但若在請求結束的時候崩潰副本沒有得到釋放,將會給內存造成極大的壓力。爲了應對這種情況我們可以採用jdbc連接池的思想,把對象放入對象池工具裏面,用"pool"來約束對象的創建與銷燬。
  首先引入Apache commons-pool依賴

		<dependency>
			<groupId>commons-pool</groupId>
			<artifactId>commons-pool</artifactId>
			<version>1.6</version>
		</dependency>

接口的聲明

//接口
import java.util.Date;
public interface FormattingService {
  String format(Date date);
  void setPattern(String pattern);
}

實現類

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.pool.BasePoolableObjectFactory;
import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.impl.GenericObjectPool;

public class PooledFormattingService implements FormattingService {

  // 對象池的實現 
  GenericObjectPool<SimpleDateFormat> dateFormatsPool;

  private static AtomicInteger count = new AtomicInteger(0);

  String pattern;
  
  public PooledFormattingService() {
  // 對象池工廠
    PoolableObjectFactory factory = new BasePoolableObjectFactory() {
      @Override
      public Object makeObject() throws Exception {
        System.out.println("創建新對象"+(count.incrementAndGet()));
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
      }
    };

    dateFormatsPool = new GenericObjectPool(factory);
    // 這裏設置創建新對象的最大數目
    dateFormatsPool.setMaxActive(10);
  }

  public String format(Date date) {
    try {
      SimpleDateFormat dateFormat =  this.dateFormatsPool.borrowObject();
      try {
        return dateFormat.format(date);
      } finally {
        this.dateFormatsPool.returnObject(dateFormat);
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public void setPattern(final String pattern) {
    PoolableObjectFactory factory = new BasePoolableObjectFactory() {
      @Override
      public Object makeObject() throws Exception {
        return new SimpleDateFormat(pattern);
      }
    };
    this.dateFormatsPool = new GenericObjectPool(factory);
  }

}

測試

import java.util.Date;

public class Mytest {

  public static void main(String[] args) {
    PooledFormattingService pooledFormattingService = new PooledFormattingService();
     //創建20個線程去調用SimpleDateFormat
     //由於對象池爲10,SimpleDateFormat最多創建10個
    for (int i = 0; i < 20; i++) {
      new Thread(() -> {
        pooledFormattingService.format(new Date());
      }).start();

    }
  }
}

核心實現都在 org.apache.commons.pool.impl.GenericObjectPool裏面
這裏只展示幾個關鍵參數

maxActive: 鏈接池中最大連接數,默認爲8.
maxIdle: 鏈接池中最大空閒的連接數,默認爲8.
minIdle: 連接池中最少空閒的連接數,默認爲0.
maxWait: 當連接池資源耗盡時,調用者最大阻塞的時間,超時將拋異常。默認永不超時.
minEvictableIdleTimeMillis: 連接空閒的最小時間,達到此值後空閒連接將可能會被移除。
softMinEvictableIdleTimeMillis: 連接空閒的最小時間,達到此值後空閒鏈接將會被移除
numTestsPerEvictionRun: 對於“空閒鏈接”檢測線程而言,每次檢測的鏈接資源的個數。默認爲3.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章