<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.4.3</version> </dependency>
使用 GenericObjectPool,有必要了解一下 GenericObjectPoolConfig,下面將說明一下其配置參數。java
/** * Provides the implementation for the common attributes shared by the * sub-classes. New instances of this class will be created using the defaults * defined by the public constants. * <p> * This class is not thread-safe. * * @since 2.0 */ public abstract class BaseObjectPoolConfig extends BaseObject implements Cloneable { private boolean lifo = DEFAULT_LIFO; private boolean fairness = DEFAULT_FAIRNESS; private long maxWaitMillis = DEFAULT_MAX_WAIT_MILLIS; private long minEvictableIdleTimeMillis = DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS; private long evictorShutdownTimeoutMillis = DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS; private long softMinEvictableIdleTimeMillis = DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS; private int numTestsPerEvictionRun = DEFAULT_NUM_TESTS_PER_EVICTION_RUN; private String evictionPolicyClassName = DEFAULT_EVICTION_POLICY_CLASS_NAME; private boolean testOnCreate = DEFAULT_TEST_ON_CREATE; private boolean testOnBorrow = DEFAULT_TEST_ON_BORROW; private boolean testOnReturn = DEFAULT_TEST_ON_RETURN; private boolean testWhileIdle = DEFAULT_TEST_WHILE_IDLE; private long timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; private boolean blockWhenExhausted = DEFAULT_BLOCK_WHEN_EXHAUSTED; private boolean jmxEnabled = DEFAULT_JMX_ENABLE; private String jmxNamePrefix = DEFAULT_JMX_NAME_PREFIX; private String jmxNameBase = DEFAULT_JMX_NAME_BASE; }
lifo
提供了後進先出(LIFO)與先進先出(FIFO)兩種行爲模式的池;
默認 DEFAULT_LIFO = true, 當池中有空閒可用的對象時,調用borrowObject方法會返回最近(後進)的實例。
org.apache.commons.pool2.impl.GenericObjectPoolapache
if (getLifo()) { idleObjects.addFirst(p); } else { idleObjects.addLast(p); }
fairness
當從池中獲取資源或者將資源還回池中時,是否使用;java.util.concurrent.locks.ReentrantLock.ReentrantLock 的公平鎖機制。
默認DEFAULT_FAIRNESS = false
org.apache.commons.pool2.impl.GenericObjectPool安全
idleObjects = new LinkedBlockingDeque<PooledObject<T>>(config.getFairness());
maxWaitMillis
當鏈接池資源用盡後,調用者獲取鏈接時的最大等待時間(單位 :毫秒);
默認值 DEFAULT_MAX_WAIT_MILLIS = -1L, 永不超時。
org.apache.commons.pool2.impl.GenericObjectPoolide
@Override public T borrowObject() throws Exception { return borrowObject(getMaxWaitMillis()); }
minEvictableIdleTimeMillis
鏈接的最小空閒時間,達到此值後該空閒鏈接可能會被移除(還需看是否已達最大空閒鏈接數);
默認值 DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS = 1000L 60L 30L
org.apache.commons.pool2.impl.GenericObjectPool性能
final EvictionConfig evictionConfig = new EvictionConfig( getMinEvictableIdleTimeMillis(), getSoftMinEvictableIdleTimeMillis(), getMinIdle());
evictorShutdownTimeoutMillis
關閉驅逐線程的超時時間;
默認值 DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS = 10L * 1000L
org.apache.commons.pool2.impl.BaseGenericObjectPoolthis
final void startEvictor(final long delay) { synchronized (evictionLock) { if (null != evictor) { EvictionTimer.cancel(evictor, evictorShutdownTimeoutMillis, TimeUnit.MILLISECONDS); evictor = null; evictionIterator = null; } if (delay > 0) { evictor = new Evictor(); EvictionTimer.schedule(evictor, delay, delay); } } }
softMinEvictableIdleTimeMillis
鏈接空閒的最小時間,達到此值後空閒連接將會被移除,且保留minIdle個空閒鏈接數;
默認值 DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = -1線程
numTestsPerEvictionRun
檢測空閒對象線程每次運行時檢測的空閒對象的數量;
若是 numTestsPerEvictionRun>=0, 則取numTestsPerEvictionRun 和池內的鏈接數 的較小值 做爲每次檢測的鏈接數;
若是 numTestsPerEvictionRun<0,則每次檢查的鏈接數是檢查時池內鏈接的總數除以這個值的絕對值再向上取整的結果;
默認值 DEFAULT_NUM_TESTS_PER_EVICTION_RUN = 3
org.apache.commons.pool2.impl.GenericObjectPoolcode
private int getNumTests() { final int numTestsPerEvictionRun = getNumTestsPerEvictionRun(); if (numTestsPerEvictionRun >= 0) { return Math.min(numTestsPerEvictionRun, idleObjects.size()); } return (int) (Math.ceil(idleObjects.size() / Math.abs((double) numTestsPerEvictionRun))); }
evictionPolicyClassName
驅逐策略的類名;
默認值 DEFAULT_EVICTION_POLICY_CLASS_NAME = "org.apache.commons.pool2.impl.DefaultEvictionPolicy"
org.apache.commons.pool2.impl.GenericObjectPoolorm
public final void setEvictionPolicyClassName(final String evictionPolicyClassName) { try { Class<?> clazz; try { clazz = Class.forName(evictionPolicyClassName, true, Thread.currentThread().getContextClassLoader()); } catch (final ClassNotFoundException e) { clazz = Class.forName(evictionPolicyClassName); } final Object policy = clazz.newInstance(); if (policy instanceof EvictionPolicy<?>) { @SuppressWarnings("unchecked") // safe, because we just checked the class final EvictionPolicy<T> evicPolicy = (EvictionPolicy<T>) policy; this.evictionPolicy = evicPolicy; } else { throw new IllegalArgumentException("[" + evictionPolicyClassName + "] does not implement EvictionPolicy"); } } catch (final ClassNotFoundException e) { throw new IllegalArgumentException("Unable to create EvictionPolicy instance of type " + evictionPolicyClassName, e); } catch (final InstantiationException e) { throw new IllegalArgumentException("Unable to create EvictionPolicy instance of type " + evictionPolicyClassName, e); } catch (final IllegalAccessException e) { throw new IllegalArgumentException("Unable to create EvictionPolicy instance of type " + evictionPolicyClassName, e); } }
testOnCreate
在建立對象時檢測對象是否有效(true : 是) , 配置true會下降性能;
默認值 DEFAULT_TEST_ON_CREATE = false。
org.apache.commons.pool2.impl.GenericObjectPool##borrowObject(final long borrowMaxWaitMillis)xml
PooledObject<T> p = null; // ...省略 // 配置true,新建立對象都會檢測對象是否有效 【 create && getTestOnCreate() 】 if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) { boolean validate = false; Throwable validationThrowable = null; try { validate = factory.validateObject(p); } catch (final Throwable t) { PoolUtils.checkRethrow(t); validationThrowable = t; } if (!validate) { try { destroy(p); destroyedByBorrowValidationCount.incrementAndGet(); } catch (final Exception e) { // Ignore - validation failure is more important } p = null; if (create) { final NoSuchElementException nsee = new NoSuchElementException("Unable to validate object"); nsee.initCause(validationThrowable); throw nsee; } } }
// 配置true,從對象池獲取對象,老是要檢測對象是否有效 【 getTestOnBorrow() 】 if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) { // ...省略 }
testOnReturn
在向對象池中歸還對象時是否檢測對象有效(true : 是) , 配置true會下降性能;
默認值 DEFAULT_TEST_ON_RETURN = false
org.apache.commons.pool2.impl.GenericObjectPool##returnObject(final T obj)
if (getTestOnReturn()) { if (!factory.validateObject(p)) { try { destroy(p); } catch (final Exception e) { swallowException(e); } try { ensureIdle(1, false); } catch (final Exception e) { swallowException(e); } updateStatsReturn(activeTime); return; } }
testWhileIdle
在檢測空閒對象線程檢測到對象不須要移除時,是否檢測對象的有效性。建議配置爲true,不影響性能,而且保證安全性;
默認值 DEFAULT_TEST_WHILE_IDLE = false
org.apache.commons.pool2.impl.GenericObjectPool##evict()
final boolean testWhileIdle = getTestWhileIdle(); // .... 代碼省略 // 配置爲true, 檢測空閒對象線程檢測到對象不須要移除時,檢測對象的有效性 if (testWhileIdle) { boolean active = false; try { factory.activateObject(underTest); active = true; } catch (final Exception e) { destroy(underTest); destroyedByEvictorCount.incrementAndGet(); } if (active) { if (!factory.validateObject(underTest)) { destroy(underTest); destroyedByEvictorCount.incrementAndGet(); } else { try { factory.passivateObject(underTest); } catch (final Exception e) { destroy(underTest); destroyedByEvictorCount.incrementAndGet(); } } } }
timeBetweenEvictionRunsMillis
空閒鏈接檢測的週期(單位毫秒);若是爲負值,表示不運行檢測線程;
默認值 DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = -1L
org.apache.commons.pool2.impl.GenericObjectPool
public GenericObjectPool(final PooledObjectFactory<T> factory, final GenericObjectPoolConfig config) { super(config, ONAME_BASE, config.getJmxNamePrefix()); if (factory == null) { jmxUnregister(); // tidy up throw new IllegalArgumentException("factory may not be null"); } this.factory = factory; idleObjects = new LinkedBlockingDeque<PooledObject<T>>(config.getFairness()); setConfig(config); // 啓動空閒鏈接檢測線程 startEvictor(getTimeBetweenEvictionRunsMillis()); }
blockWhenExhausted
當對象池沒有空閒對象時,新的獲取對象的請求是否阻塞(true 阻塞,maxWaitMillis 才生效; false 鏈接池沒有資源立馬拋異常)
默認值 DEFAULT_BLOCK_WHEN_EXHAUSTED = true
org.apache.commons.pool2.impl.GenericObjectPool##borrowObject(final long borrowMaxWaitMillis)
final boolean blockWhenExhausted = getBlockWhenExhausted(); // ... 省略 if (blockWhenExhausted) { if (p == null) { if (borrowMaxWaitMillis < 0) { p = idleObjects.takeFirst(); } else { p = idleObjects.pollFirst(borrowMaxWaitMillis, TimeUnit.MILLISECONDS); } } if (p == null) { throw new NoSuchElementException( "Timeout waiting for idle object"); } }
jmxEnabled
是否註冊JMX
默認值 DEFAULT_JMX_ENABLE = true
org.apache.commons.pool2.impl.BaseGenericObjectPool
public BaseGenericObjectPool(final BaseObjectPoolConfig config, final String jmxNameBase, final String jmxNamePrefix) { if (config.getJmxEnabled()) { this.oname = jmxRegister(config, jmxNameBase, jmxNamePrefix); } else { this.oname = null; } // Populate the creation stack trace this.creationStackTrace = getStackTrace(new Exception()); // save the current TCCL (if any) to be used later by the evictor Thread final ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { factoryClassLoader = null; } else { factoryClassLoader = new WeakReference<ClassLoader>(cl); } fairness = config.getFairness(); }
jmxNamePrefix
JMX前綴
默認值 DEFAULT_JMX_NAME_PREFIX = "pool"
org.apache.commons.pool2.impl.GenericObjectPool
// JMX specific attributes private static final String ONAME_BASE = "org.apache.commons.pool2:type=GenericObjectPool,name="; public GenericObjectPool(final PooledObjectFactory<T> factory, final GenericObjectPoolConfig config) { // 參見上述 jmxEnabled 部分 super(config, ONAME_BASE, config.getJmxNamePrefix()); // ..... }
jmxNameBase
使用 base + jmxNamePrefix + i 來生成ObjectName
默認值 DEFAULT_JMX_NAME_BASE = null,GenericObjectPool 構造方法使用 ONAME_BASE 初始化。
private ObjectName jmxRegister(final BaseObjectPoolConfig config,final String jmxNameBase, String jmxNamePrefix) { ObjectName objectName = null; final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); int i = 1; boolean registered = false; String base = config.getJmxNameBase(); if (base == null) { base = jmxNameBase; } while (!registered) { try { ObjectName objName; if (i == 1) { objName = new ObjectName(base + jmxNamePrefix); } else { objName = new ObjectName(base + jmxNamePrefix + i); } mbs.registerMBean(this, objName); objectName = objName; registered = true; } catch (final MalformedObjectNameException e) { if (BaseObjectPoolConfig.DEFAULT_JMX_NAME_PREFIX.equals(jmxNamePrefix) && jmxNameBase.equals(base)) { // 若是走到這步,就跳過jmx註冊,應該不會發生 registered = true; } else { // 前者使用的名稱不是默認配置,則使用默認配置替代 jmxNamePrefix = BaseObjectPoolConfig.DEFAULT_JMX_NAME_PREFIX; base = jmxNameBase; } } catch (final InstanceAlreadyExistsException e) { // 增長一個索引,再試一次 i++; } catch (final MBeanRegistrationException e) { // 若是走到這步,就跳過jmx註冊,應該不會發生 registered = true; } catch (final NotCompliantMBeanException e) { // 若是走到這步,就跳過jmx註冊,應該不會發生 registered = true; } } return objectName; }
/** * A simple "struct" encapsulating the configuration for a * {@link GenericObjectPool}. * * <p> * This class is not thread-safe; it is only intended to be used to provide * attributes used when creating a pool. * * @since 2.0 */ public class GenericObjectPoolConfig extends BaseObjectPoolConfig { private int maxTotal = DEFAULT_MAX_TOTAL; private int maxIdle = DEFAULT_MAX_IDLE; private int minIdle = DEFAULT_MIN_IDLE; }