版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/j16421881/article/details/78764287
关于对象池的概念这里不做过多解释,除了用来缓存数据库连接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.