最后
现在正是金三银四的春招高潮,前阵子小编一直在搭建自己的网站,并整理了全套的**【一线互联网大厂Java核心面试题库+解析】:包括Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等**
p = create();
if (p != null) {
create = true;
}
}
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”);
}
} else {
if (p == null) {
throw new NoSuchElementException(“Pool exhausted”);
}
}
if (!p.allocate()) {
p = null;
}
if (p != null) {
try {
factory.activateObject§;
} catch (final Exception e) {
try {
destroy(p, DestroyMode.NORMAL);
} catch (final Exception e1) {
// Ignore - activation failure is more important
}
p = null;
if (create) {
final NoSuchElementException nsee = new NoSuchElementException(
“Unable to activate object”);
nsee.initCause(e);
throw nsee;
}
}
if (p != null && getTestOnBorrow()) {
boolean validate = false;
Throwable validationThrowable = null;
try {
validate = factory.validateObject§;
} catch (final Throwable t) {
PoolUtils.checkRethrow(t);
validationThrowable = t;
}
if (!validate) {
try {
destroy(p, DestroyMode.NORMAL);
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;
}
}
}
}
}
updateStatsBorrow(p, System.currentTimeMillis() - waitTime);
return p.getObject();
}
3.工厂的passivateObject(PooledObject p)和passivateObject(PooledObject p)即对象的激活和钝化方法有什么用?
如图在对象使用完被返回对象池时,如果校验失败直接销毁,如果校验通过需要先钝化对象再存入空闲队列。至于激活对象的方法在上述取用对象时也会先激活再被取出。因此我们可以发现处于空闲和使用中的对象他们除了状态不一致,我们也可以通过激活和钝化的方式在他们之间增加新的差异,例如我们要做一个Elasticsearch连接池,每个对象就是一个带有ip和端口的连接实例,很显然访问es集群是多个不同的ip,所以每次访问的ip不一定相同,我们则可以在激活操作为对象赋值ip和端口,钝化操作中将ip和端口归为默认值或者空,这样流程更为标准。
public void returnObject(final T obj) {
final PooledObject p = allObjects.get(new IdentityWrapper<>(obj));
//…
//校验失败直接销毁 return
//…
try {
factory.passivateObject§;
} catch (final Exception e1) {
swallowException(e1);
try {
destroy(p, DestroyMode.NORMAL);
} catch (final Exception e) {
swallowException(e);
}
try {
ensureIdle(1, false);
} catch (final Exception e) {
swallowException(e);
}
updateStatsReturn(activeTime);
return;
}
//…
//返回空闲队列
}
对象池相关配置项
========
对象池提供了许多配置项,在我们使用的GenericObjectPool默认基础对象池中可以通过构造方法传参传入GenericObjectPoolConfig,当然我们也可以看GenericObjectPoolConfig底层实现的基础类BaseObjectPoolConfig,具体包含如下配置:
-
maxTotal:对象池中最大使用数量,默认为8
-
maxIdle:对象中空闲对象最大数量,默认为8
-
minIdle:对象池中空闲对象最小数量,默认为8
-
lifo:当去获取对象池中的空闲实例时,是否需要遵循后进先出的原则,默认为true
-
blockWhenExhausted:当对象池处于exhausted状态,即可用实例为空时,是否阻塞来获取实例的线程,默认 true
-
fairness:当对象池处于exhausted状态,即可用实例为空时,大量线程在同时阻塞等待获取可用的实例,fairness配置来控制是否启用公平锁算法,即先到先得,默认为false。这一项的前提是blockWhenExhausted配置为true
-
maxWaitMillis:最大阻塞时间,当对象池处于exhausted状态,即可用实例为空时,大量线程在同时阻塞等待获取可用的实例,如果阻塞时间超过了maxWaitMillis将会抛出异常。当此值为负数时,代表无限期阻塞直到可用。默认为-1
-
testOnCreate:创建对象前是否校验(即调用工厂的validateObject()方法),如果检验失败,那么borrowObject()返回将失败,默认为false
-
testOnBorrow:取用对象前是否检验,默认为false
-
testOnReturn:返回对象池前是否检验,即调用工厂的returnObject(),若检验失败会销毁对象而不是返回池中,默认为false
-
timeBetweenEvictionRunsMillis:驱逐周期,默认为-1代表不进行驱逐测试
-
testWhileIdle:处于idle队列中即闲置的对象是否被驱逐器进行驱逐验证,当该对象上次运行时间距当前超过了setTimeBetweenEvictionRunsMillis(long))设置的值,将会被驱逐验证,调用validateObject()方法,若验证成功,对象将会销毁。默认为false
使用步骤
====
-
创建工厂类:通过继承BaseGenericObjectPool或者实现基础接口PooledObjectFactory,并按照业务需求重写对象的创建、销毁、校验、激活、钝化方法,其中销毁多为连接的关闭、置空等。
-
创建池:通过继承GenericObjectPool或者实现基础接口ObjectPool,建议使用前者,它为我们提供了空闲对象驱逐检测机制(即将空闲队列中长时间未使用的对象销毁,降低内存占用),以及提供了很多对象的基本信息,例如对象最后被使用的时间、使用对象前是否检验等。
-
创建池相关配置(可选):通过继承GenericObjectPoolConfig或者继承BaseObjectPoolConfig,来增加对线程池的配置控制,建议使用前者,它为我们实现了基本方法,只需要自己添加需要的属性即可。
-
创建包装类(可选):即要存在于对象池中的对象,在实际对象之外添加许多基础属性,便于了解对象池中对象的实时状态。
注意事项
====
我们虽然使用了默认实现,但是也应该结合实际生产情况进行优化,不能使用了线程池而性能却更低了。在使用中我们应注意以下事项:
- 要为对象池设置空闲队列最大最小值,默认最大最小值,默认最大为8往往不能满足需要
private volatile int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE;
private volatile int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE;
public static final int DEFAULT_MAX_IDLE = 8;
public static final int DEFAULT_MIN_IDLE = 0;
-
对象池设置maxWaitMillis属性,即取用对象最大等待时间
-
使用完对象及时释放对象,将对象返回池中,特别是发生了异常也要通过try…chatch…finally的方式确保释放,避免占用资源
我们展开讲讲注意事项,首先为什么要设置maxWaitMillis,我们取用对象使用的如下方法
public T borrowObject() throws Exception {
return borrowObject(getMaxWaitMillis());
}
可以看到默认的最大等待时间为-1L
private volatile long maxWaitMillis =
BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS;
//…
public static final long DEFAULT_MAX_WAIT_MILLIS = -1L;
我们再来查看取用对象逻辑,blockWhenExhausted默认为true,意思是当池中不存在空闲对象时,又来取用对象,线程将会被阻塞直到有新的可用对象。从上我们得知-1L将会执行idleObjects.takeFirst()
public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
//…
final boolean blockWhenExhausted = getBlockWhenExhausted();
boolean create;
final long waitTime = System.currentTimeMillis();
while (p == null) {
//…
if (blockWhenExhausted) {
if (p == null) {
if (borrowMaxWaitMillis < 0) {
p = idleObjects.takeFirst();
} else {
p = idleObjects.pollFirst(borrowMaxWaitMillis,
TimeUnit.MILLISECONDS);
}
}
}
}
}
如下,阻塞队列将会一直阻塞,直到有了空闲对象才停止阻塞,这样的设定将会在吞吐提高时造成大面积阻塞影响
public E takeFirst() throws InterruptedException {
lock.lock();
try {
E x;
while ( (x = unlinkFirst()) == null) {
notEmpty.await();
}
return x;
} finally {
lock.unlock();
}
}
还有一个注意事项就是要记得回收资源,即调用public void returnObject(final T obj)方法,原因显而易见,对象池对我们是否使用完了对象是无感知的,需要我们调用该方法回收对象,特别是发生异常也要保证回收,因此最佳实践如下:
try{
item = pool.borrowObject();
} catch(Exception e) {
log.error(“…”);
} finally {
pool.returnObject(item);
}
实例使用
====
实例1:实现一个简单的字符串池
===============
创建字符串工厂
package com.anqi.demo.demopool2.pool.fac;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
/**
- 字符串池工厂
*/
public class StringPoolFac extends BasePooledObjectFactory {
public StringPoolFac() {
super();
}
@Override
public String create() throws Exception {
return “str-val-”;
}
@Override
public PooledObject wrap(String s) {
return new DefaultPooledObject<>(s);
}
@Override
public void destroyObject(PooledObject p) throws Exception {
}
@Override
public boolean validateObject(PooledObject p) {
return super.validateObject§;
}
@Override
public void activateObject(PooledObject p) throws Exception {
super.activateObject§;
}
@Override
public void passivateObject(PooledObject p) throws Exception {
super.passivateObject§;
}
}
创建字符串池
package com.anqi.demo.demopool2.pool;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
/**
- 字符串池
*/
public class StringPool extends GenericObjectPool {
public StringPool(PooledObjectFactory factory) {
super(factory);
}
public StringPool(PooledObjectFactory factory, GenericObjectPoolConfig config) {
super(factory, config);
最后
整理的这些资料希望对Java开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
其实面试这一块早在第一个说的25大面试专题就全都有的。以上提及的这些全部的面试+学习的各种笔记资料,我这差不多来回搞了三个多月,收集整理真的很不容易,其中还有很多自己的一些知识总结。正是因为很麻烦,所以对以上这些学习复习资料感兴趣,
StringPool(PooledObjectFactory factory, GenericObjectPoolConfig config) {
super(factory, config);
最后
整理的这些资料希望对Java开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
[外链图片转存中…(img-GwjZpCZk-1714995259075)]
[外链图片转存中…(img-KKtmzUIZ-1714995259076)]
其实面试这一块早在第一个说的25大面试专题就全都有的。以上提及的这些全部的面试+学习的各种笔记资料,我这差不多来回搞了三个多月,收集整理真的很不容易,其中还有很多自己的一些知识总结。正是因为很麻烦,所以对以上这些学习复习资料感兴趣,