当前位置:首页 > PHP教程 > PHP总结归纳

使用JakartaCommonsPool对象池技术

1. 为什么使用对象池技术 创建新的对象并初始化,可能会消耗很多时间。在这种对象的初始化工作中如果依赖一些rpc远程调用来创建对象,例如通过socket或者http连接远程服务资源,最典型的就是数据库服务以及远程队列(remote queue),建立连接 - 发送数据 -

1. 为什么使用对象池技术

创建新的对象并初始化,可能会消耗很多时间。在这种对象的初始化工作中如果依赖一些rpc远程调用来创建对象,例如通过socket或者http连接远程服务资源,最典型的就是数据库服务以及远程队列(remote queue),建立连接 -> 发送数据 -> 接收连接 -> 释放连接的过程无疑对于客服端来说相当繁重。在需要大量或者频繁生成这样的对象的时候,就可能会对性能造成一些不可忽略的影响。要解决这个问题在软件层面上可以使用对象池技术(object pooling),而jakarta commons pool框架则是处理对象池化的有力外援。

2. 对象池技术解释

对象池的基本思路是:将用过的对象保存起来,等下一次需要这种对象的时候,再拿出来重复使用,从而在一定程度上减少频繁创建对象所造成的开销。用于充当保存对象的“容器”的对象,被称为“对象池”(object pool,或简称pool)。

并非所有对象都适合拿来池化――因为维护对象池也要造成一定开销。对生成时开销不大的对象进行池化,反而可能会出现“维护对象池的开销”大于“生成新对象的开销”,从而使性能降低的情况。但是对于生成时开销可观的对象,池化技术就是提高性能的有效策略了。

3. jakarta commons pool对象池框架

在该框架中,主要工作有两类对象:

poolableobjectfactory:用于管理被池化的对象的产生、激活、挂起、校验和销毁;

objectpool:用于管理要被池化的对象的借出和归还,并通知poolableobjectfactory完成相应的工作;

相应地,使用pool框架的过程,也就划分成“创立poolableobjectfactory”、“使用objectpool”两种动作。

3.1 使用poolableobjectfactory

pool框架利用poolableobjectfactory来管理被池化的对象。objectpool的实例在需要处理被池化的对象的产生、激活、挂起、校验和销毁工作时,就会调用跟它关联在一起的poolableobjectfactory实例的相应方法来操作。

poolableobjectfactory是在org.apache.commons.pool包中定义的一个接口。实际使用的时候需要利用这个接口的一个具体实现。pool框架本身没有包含任何一种poolableobjectfactory实现,需要根据情况自行创立。

创立poolableobjectfactory的大体步骤是:

创建一个实现了poolableobjectfactory接口的类。

import org.apache.commons.pool.poolableobjectfactory;

public class poolableobjectfactorysample

implements poolableobjectfactory {

private static int counter = 0;

}

为这个类添加一个object makeobject()方法。这个方法用于在必要时产生新的对象。

public object makeobject() throws exception {

object obj = string.valueof(counter++);

system.err.println("making object " + obj);

return obj;

}

为这个类添加一个void activateobject(object obj)方法。这个方法用于将对象“激活”――设置为适合开始使用的状态。

public void activateobject(object obj) throws exception {

system.err.println("activating object " + obj);

}

为这个类添加一个void passivateobject(object obj)方法。这个方法用于将对象“挂起”――设置为适合开始休眠的状态。

public void passivateobject(object obj) throws exception {

system.err.println("passivating object " + obj);

}

为这个类添加一个boolean validateobject(object obj)方法。这个方法用于校验一个具体的对象是否仍然有效,已失效的对象会被自动交给destroyobject方法销毁

public boolean validateobject(object obj) {

boolean result = (math.random() > 0.5);

system.err.println("validating object "

+ obj + " : " + result);

return result;

}

为这个类添加一个void destroyobject(object obj)方法。这个方法用于销毁被validateobject判定为已失效的对象。

public void destroyobject(object obj) throws exception {

system.err.println("destroying object " + obj);

}

最后完成的poolableobjectfactory类似这个样子:

import org.apache.commons.pool.poolableobjectfactory; public class poolableobjectfactorysample implements poolableobjectfactory { private static int counter = 0; public object makeobject() throws exception { object obj = string.valueof(counter++); system.err.println("making object " + obj); return obj; } public void activateobject(object obj) throws exception { system.err.println("activating object " + obj); } public void passivateobject(object obj) throws exception { system.err.println("passivating object " + obj); } public boolean validateobject(object obj) { /* 以1/2的概率将对象判定为失效 */ boolean result = (math.random() > 0.5); system.err.println("validating object " + obj + " : " + result); return result; } public void destroyobject(object obj) throws exception { system.err.println("destroying object " + obj); } }

3.2 使用objectpool

有了合适的poolableobjectfactory之后,便可以开始请出objectpool来与之同台演出了。

objectpool是在org.apache.commons.pool包中定义的一个接口,实际使用的时候也需要利用这个接口的一个具体实现。pool框架本身包含了若干种现成的objectpool实现,可以直接利用。如果都不合用,也可以根据情况自行创建。具体的创建方法,可以参看pool框架的文档和源码。

objectpool的使用方法类似这样:

生成一个要用的poolableobjectfactory类的实例。

poolableobjectfactory factory = new poolableobjectfactorysample();

利用这个poolableobjectfactory实例为参数,生成一个实现了objectpool接口的类(例如stackobjectpool)的实例,作为对象池。

objectpool pool = new stackobjectpool(factory);

需要从对象池中取出对象时,调用该对象池的object borrowobject()方法。

object obj = null;

obj = pool.borrowobject();

需要将对象放回对象池中时,调用该对象池的void returnobject(object obj)方法。

pool.returnobject(obj);

当不再需要使用一个对象池时,调用该对象池的void close()方法,释放它所占据的资源。

pool.close();

这些操作都可能会抛出异常,需要另外处理。

比较完整的使用objectpool的全过程,可以参考这段代码:

import org.apache.commons.pool.objectpool; import org.apache.commons.pool.poolableobjectfactory; import org.apache.commons.pool.impl.stackobjectpool; public class objectpoolsample { public static void main(string[] args) { object obj = null; poolableobjectfactory factory = new poolableobjectfactorysample(); objectpool pool = new stackobjectpool(factory); try { for(long i = 0; i < 100 ; i++) { system.out.println("== " + i + " =="); obj = pool.borrowobject(); system.out.println(obj); pool.returnobject(obj); } obj = null;//明确地设为null,作为对象已归还的标志 } catch (exception e) { e.printstacktrace(); } finally { try{ if (obj != null) {//避免将一个对象归还两次 pool.returnobject(obj); } pool.close(); } catch (exception e){ e.printstacktrace(); } } } }

综上,uml图如下:

3.3 线程安全问题

有时候可能要在多线程环境下使用pool框架,这时候就会遇到和pool框架的线程安全程度有关的问题。

因为objectpool和keyedobjectpool都是在org.apache.commons.pool中定义的接口,而在接口中无法使用“synchronized”来修饰方法,所以,一个objectpool下的各个方法是否是同步方法,完全要看具体的实现。而且,单纯地使用了同步方法,也并不能使对象就此在多线程环境里高枕无忧。

就pool框架中自带的几个objectpool的实现而言,它们都在一定程度上考虑了在多线程环境中使用的情况。不过还不能说它们是完全“线程安全”的。

例如,这段代码有些时候就会有一些奇怪的表现,最后输出的结果比预期的要大:

import org.apache.commons.pool.objectpool; import org.apache.commons.pool.impl.stackobjectpool; class unsafepicker extends thread { private objectpool pool; public unsafepicker(objectpool op) { pool = op; } public void run() { object obj = null; try { /* 似乎…… */ if ( pool.getnumactive() < 5 ) { sleep((long) (math.random() * 10)); obj = pool.borrowobject(); } } catch (exception e) { e.printstacktrace(); } } } public class unsafemultithreadpoolingsample { public static void main(string[] args) { objectpool pool = new stackobjectpool (new basepoolableobjectfactorysample()); thread ts[] = new thread[20]; for (int j = 0; j < ts.length; j++) { ts[j] = new unsafepicker(pool); ts[j].start(); } try { thread.sleep(1000); /* 然而…… */ system.out.println("numactive:" + pool.getnumactive()); } catch (exception e) { e.printstacktrace(); } } }

要避免这种情况,就要进一步采取一些措施才行:

import org.apache.commons.pool.objectpool; import org.apache.commons.pool.impl.stackobjectpool; class safepicker extends thread { private objectpool pool; public safepicker(objectpool op) { pool = op; } public void run() { object obj = null; try { /* 略加处理 */ synchronized (pool) { if ( pool.getnumactive() < 5 ) { sleep((long) (math.random() * 10)); obj = pool.borrowobject(); } } } catch (exception e) { e.printstacktrace(); } } } public class safemultithreadpoolingsample { public static void main(string[] args) { objectpool pool = new stackobjectpool (new basepoolableobjectfactorysample()); thread ts[] = new thread[20]; for (int j = 0; j < ts.length; j++) { ts[j] = new safepicker(pool); ts[j].start(); } try { thread.sleep(1000); system.out.println("numactive:" + pool.getnumactive()); } catch (exception e) { e.printstacktrace(); } } }

基本上,可以说pool框架是线程相容的。但是要在多线程环境中使用,还需要作一些特别的处理。

4. jedis中线程池的实例

下面看一个实例,由于近期在研究redis,所以需要找一个可靠的redis驱动,有很多开源项目,详见链接,jedis便是其中历史较早的。相比于其他驱动,jedis提供了一个jedispool用于管理redis连接的池,其主要工作的包括pool.java,jedispool.java和jedispoolconfig.java。

pool.java封装了一个genericobjectpool,负责jedis连接的产生、校验和销毁。

package redis.clients.util; ? import org.apache.commons.pool.poolableobjectfactory; import org.apache.commons.pool.impl.genericobjectpool; import redis.clients.jedis.exceptions.jedisconnectionexception; import redis.clients.jedis.exceptions.jedisexception; ? public abstract class pool { private final genericobjectpool internalpool; ? public pool(final genericobjectpool.config poolconfig, poolableobjectfactory factory) { this.internalpool = new genericobjectpool(factory, poolconfig); } ? @suppresswarnings("unchecked") public t getresource() { try { return (t) internalpool.borrowobject(); } catch (exception e) { throw new jedisconnectionexception( "could not get a resource from the pool", e); } } ? public void returnresourceobject(final object resource) { try { internalpool.returnobject(resource); } catch (exception e) { throw new jedisexception( "could not return the resource to the pool", e); } } ? public void returnbrokenresource(final t resource) { returnbrokenresourceobject(resource); } ? public void returnresource(final t resource) { returnresourceobject(resource); } ? protected void returnbrokenresourceobject(final object resource) { try { internalpool.invalidateobject(resource); } catch (exception e) { throw new jedisexception( "could not return the resource to the pool", e); } } ? public void destroy() { try { internalpool.close(); } catch (exception e) { throw new jedisexception("could not destroy the pool", e); } } }

jedispool.java继承了pool.java,内部写了一个inner class – basepoolableobjectfactory,用于新建jedispool实例时传入线程池建立、销毁、验证连接的基本方法。

package redis.clients.jedis; ? import org.apache.commons.pool.basepoolableobjectfactory; import org.apache.commons.pool.impl.genericobjectpool.config; ? import redis.clients.util.pool; ? public class jedispool extends pool { ? public jedispool(final config poolconfig, final string host) { this(poolconfig, host, protocol.default_port, protocol.default_timeout, null, protocol.default_database); } ? public jedispool(string host, int port) { this(new config(), host, port, protocol.default_timeout, null, protocol.default_database); } ? public jedispool(final string host) { this(host, protocol.default_port); } ? public jedispool(final config poolconfig, final string host, int port, int timeout, final string password) { this(poolconfig, host, port, timeout, password, protocol.default_database); } ? public jedispool(final config poolconfig, final string host, final int port) { this(poolconfig, host, port, protocol.default_timeout, null, protocol.default_database); } ? public jedispool(final config poolconfig, final string host, final int port, final int timeout) { this(poolconfig, host, port, timeout, null, protocol.default_database); } ? public jedispool(final config poolconfig, final string host, int port, int timeout, final string password, final int database) { super(poolconfig, new jedisfactory(host, port, timeout, password, database)); } ? ? public void returnbrokenresource(final binaryjedis resource) { returnbrokenresourceobject(resource); } ? public void returnresource(final binaryjedis resource) { returnresourceobject(resource); } ? /** * poolableobjectfactory custom impl. */ private static class jedisfactory extends basepoolableobjectfactory { private final string host; private final int port; private final int timeout; private final string password; private final int database; ? public jedisfactory(final string host, final int port, final int timeout, final string password, final int database) { super(); this.host = host; this.port = port; this.timeout = timeout; this.password = password; this.database = database; } ? public object makeobject() throws exception { final jedis jedis = new jedis(this.host, this.port, this.timeout); ? jedis.connect(); if (null != this.password) { jedis.auth(this.password); } if( database != 0 ) { jedis.select(database); } ? return jedis; } ? public void destroyobject(final object obj) throws exception { if (obj instanceof jedis) { final jedis jedis = (jedis) obj; if (jedis.isconnected()) { try { try { jedis.quit(); } catch (exception e) { } jedis.disconnect(); } catch (exception e) { ? } } } } ? public boolean validateobject(final object obj) { if (obj instanceof jedis) { final jedis jedis = (jedis) obj; try { return jedis.isconnected() && jedis.ping().equals("pong"); } catch (final exception e) { return false; } } else { return false; } } } }

jedispoolconfig继承了genericobjectpool.config,用于指定一些线程池初始化参数。

package redis.clients.jedis; ? import org.apache.commons.pool.impl.genericobjectpool.config; ? /** * subclass of org.apache.commons.pool.impl.genericobjectpool.config that * includes getters/setters so it can be more easily configured by spring and * other ioc frameworks. * * spring example: * * * * * * * for information on parameters refer to: * * http://commons.apache.org/pool/apidocs/org/apache/commons/pool/impl/ * genericobjectpool.html */ public class jedispoolconfig extends config { public jedispoolconfig() { // defaults to make your life with connection pool easier :) settestwhileidle(true); setminevictableidletimemillis(60000); settimebetweenevictionrunsmillis(30000); setnumtestsperevictionrun(-1); } ? public int getmaxidle() { return maxidle; } ? public void setmaxidle(int maxidle) { this.maxidle = maxidle; } ? public int getminidle() { return minidle; } ? public void setminidle(int minidle) { this.minidle = minidle; } ? public int getmaxactive() { return maxactive; } ? public void setmaxactive(int maxactive) { this.maxactive = maxactive; } ? public long getmaxwait() { return maxwait; } ? public void setmaxwait(long maxwait) { this.maxwait = maxwait; } ? public byte getwhenexhaustedaction() { return whenexhaustedaction; } ? public void setwhenexhaustedaction(byte whenexhaustedaction) { this.whenexhaustedaction = whenexhaustedaction; } ? public boolean istestonborrow() { return testonborrow; } ? public void settestonborrow(boolean testonborrow) { this.testonborrow = testonborrow; } ? public boolean istestonreturn() { return testonreturn; } ? public void settestonreturn(boolean testonreturn) { this.testonreturn = testonreturn; } ? public boolean istestwhileidle() { return testwhileidle; } ? public void settestwhileidle(boolean testwhileidle) { this.testwhileidle = testwhileidle; } ? public long gettimebetweenevictionrunsmillis() { return timebetweenevictionrunsmillis; } ? public void settimebetweenevictionrunsmillis( long timebetweenevictionrunsmillis) { this.timebetweenevictionrunsmillis = timebetweenevictionrunsmillis; } ? public int getnumtestsperevictionrun() { return numtestsperevictionrun; } ? public void setnumtestsperevictionrun(int numtestsperevictionrun) { this.numtestsperevictionrun = numtestsperevictionrun; } ? public long getminevictableidletimemillis() { return minevictableidletimemillis; } ? public void setminevictableidletimemillis(long minevictableidletimemillis) { this.minevictableidletimemillis = minevictableidletimemillis; } ? public long getsoftminevictableidletimemillis() { return softminevictableidletimemillis; } ? public void setsoftminevictableidletimemillis( long softminevictableidletimemillis) { this.softminevictableidletimemillis = softminevictableidletimemillis; } ? }

1. 为什么使用对象池技术 创建新的对象并初始化,可能会消耗很多时间。在这种对象的初始化工作中如果依赖一些rpc远程调用来创建对象,例如通过socket或者http连接远程服务资源,最典型的就是数据库服务以及远程队列(remote queue),建立连接 -> 发送数据 -> 接收连接 [......].syntaxhighlighter{padding-top:20px;padding-bottom:20px;}

【说明】本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:)!