JAVA的那些池子

1、数据库连接池

JDBC在JDK中有一整套API,也就是接口。对于不同的数据库都有自己的实现。但是这些实现都是单个的连接,如果一个应用只有一个连接,那不能支持并发,如果一个操作就有一个连接,那连接的开销又非常大。
最好的办法就是一个线程一个连接,或者是总共建立30个连接,你用完了它用。这就是JDBC连接池的做法。

连接池要取出连接,再放回去,当连接不够的时候怎么办,连接闲置的时候怎么办,不同的连接池算法不一样,实现方式不一样,那连接池的开销也就是效率不一样。这个需要你自己去选择,不过连接池的实现原理大致都是相同的,就是实现JDK中的javax.sql.DataSource接口,通过javax.sql.DataSource.getConnection(),获取一个连接java.sql.Connection,也就是从池子里取出一个连接,这个Connection也由连接池去实现,在Connection.close()的时候,并没有关闭连接,而是把连接放回到池子中,从而达到池子的效果。

所以连接池的配置关键是DataSource的配置,等DataSource配好了,对连接池的使用,就像基础的JDBC连接一样使用。一般DataSource的配置都大同小异,都是那几个参数,参数的含义,网上都可以搜的到,下面是阿里巴巴的Druid数据库连接池的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<bean id="dataSource">
    <property name="driverClassName"  
                  value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc1.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
    <property name="maxActive" value="${jdbc.maxActive}" />
    <property name="initialSize" value="${jdbc.initialSize}" />
    <property name="maxWait" value="${jdbc.maxWait}" />
    <property name="minIdle" value="${jdbc.minIdle}" />
    <property name="timeBetweenEvictionRunsMillis" 
                    value="${jdbc.timeBetweenEvictionRunsMillis}" />
    <property name="minEvictableIdleTimeMillis" 
                       value="${jdbc.minEvictableIdleTimeMillis}" />
    <property name="validationQuery" 
                                  value="${jdbc.validationQuery}" />
    <property name="testWhileIdle" 
                                    value="${jdbc.testWhileIdle}" />
    <property name="testOnBorrow" value="${jdbc.testOnBorrow}" />
    <property name="testOnReturn" value="${jdbc.testOnReturn}" />
    <property name="defaultAutoCommit" 
                                 value="${jdbc.defaultAutoCommit}"/>
</bean>

2、HTTP连接池

HTTP连接,有一个keepalive的参数,只要你带了这个参数并且服务器支持这个参数,那么你就可以在timeout的时间内保持和服务器的连接,不断的发送请求,得到响应。这样你喝服务器的交互,就不用每次请求都去进行TCP的连接的三次握手等等,减少连接等待时间。所以如果你经常和一个服务器进行http交互的话,使用HTTP连接池是个不错的选择。

HTTP连接池的原理就是利用了keepalive的参数,保持几个或者多个服务器的连接,当你下次在有效的时间内去同一个服务器请求的时候,就会减少不小的开支。这就是为什么浏览器浏览一个网页,第一个请求可能很慢,后面的请求会很快,一方面可能是因为DNS初次解析的缘故,另一方面有可能就是keepalive参数的效果。

HTTP连接池的实现也有很多种,而且一般的HTTP包里的client都默认实现了连接池,你可以给它配置不同需求的连接池,这里示例apache的httpclient4X的连接池:org.apache.http.impl.conn.PoolingClientConnectionManager

1
2
3
4
5
6
7
8
9
10
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(
    new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
schemeRegistry.register(
    new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
 
PoolingClientConnectionManager connectionManager =
               new PoolingClientConnectionManager(schemeRegistry);
connectionManager.setMaxTotal(maxConnections);
connectionManager.setDefaultMaxPerRoute(maxPerRoute);

把连接池管理器配给client即可

1
2
DefaultHttpClient httpClient = 
              new DefaultHttpClient(connectionManager, params);

其中params 是你要配的其它参数,例如连接超时时间,请求超时时间等等。

3、线程池

如果你的任务需要并发执行,那么你就需要建立新的线程去执行,但是线程的建立是需要开销的,而且系统的线程数是有限制的,你不可能起无数个线程去执行任务的。如果你的线程的任务需要很大的内存或者带宽,起很多线程也是有限制的,所以你要用到线程池的概念。JDK就有丰富多彩的线程池实现,你直接可以使用,也就是java.util.concurrent.ExecutorService的实现类。concurrent字面上就是并发的意思。
最常用的就是java.util.concurrent.ThreadPoolExecutor。
你可以直接new出一个来就可以

1
2
3
4
5
6
RejectedExecutionHandler rejectedExecutionHandler = 
                 new ThreadPoolExecutor.AbortPolicy();
BlockingQueue queue= new LinkedBlockingQueue(queueCapacity);
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
corePoolSize, maxPoolSize, keepAliveSeconds, TimeUnit.SECONDS, 
queue, rejectedExecutionHandler);

当maxPoolSize个线程都用完了的时候,新添加的任务就会放到queue中,如果queue中都满了得话
就会按rejectedExecutionHandler
你选择的策略进行处理。所以你要根据你的业务量,合理安排pool的大小和queue的大小。

在使用的时候,只要向threadPool提交你的任务就可以了:

1
2
3
4
5
6
threadPool.submit(new Runable(){
	public void run()
	{
 
	}
});

4、普通对象池
如果你的一个对象的创建需要很大的开销,那么你需要使用对象池,创建一些对象放在池子里,用的时候取出来一个,不用的时候放回去。对象池也有现成的方案,那就是apache的commons-pool。
比较常用的是org.apache.commons.pool.impl.GenericObjectPool
你需要实现一个org.apache.commons.pool.PoolableObjectFactory接口中的makeObject,
activateObject, passivateObject, validateObject,
destroyObject方法。说明你的对象是如何实现的,是否是有效的对象,销毁这个对象需要说明操作等等。

然后new一个对象池就可以了,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
GenericObjectPool objectPool = 
       new PoolableObjectFactory(objectFactory);
 
//在使用的时候是这样的形式:
MyClass borrowObject = null;
try
{
    borrowObject = objectPool.borrowObject();
    //do any thing
}
finally
{
    if(borrowObject != null)
    {
        objectPool.returnObject(borrowObject);
    }
}

留言

提示:你的email不会被公布,欢迎留言^_^

*

验证码 *