本文共 8814 字,大约阅读时间需要 29 分钟。
源代码Git地址:
Object Pool,即对象池,对象被预先创建并初始化后放入对象池中,对象提供者就能利用已有的对象来处理请求,减少对象频繁创建所占用的内存空间和初始化时间,例如数据库连接对象基本上都是创建后就被放入连接池中,后续的查询请求使用的是连接池中的对象,从而加快了查询速度。类似被放入对象池中的对象还包括Socket对象、线程对象和绘图对象(GDI对象)等。
在Object Pool设计模式中,主要有两个参与者:对象池的管理者和对象池的用户,用户从管理者那里获取对象,对象池对于用户来讲是透明的,但是用户必须遵守这些对象的使用规则,使用完对象后必须归还或者关闭对象,例如数据库连接对象使用完后必须关闭,否则该对象就会被一直占用着。
对象管理者需要维护对象池,包括初始化对象池、扩充对象池的大小、重置归还对象的状态等。
对象池在被初始化时,可能只有几个对象,甚至没有对象,按需创建对象能节省资源和时间,对于响应时间要求较高的情况,可以预先创建若干个对象。当对象池中没有对象可供使用时,管理者一般需要使用某种策略来扩充对象池,比如将对象池的大小翻倍。另外,在多线程的情况下,可以让请求资源的线程等待,直到其他线程归还了占用的对象。
一般来说,对象池中的对象在逻辑状态上是相同的,如果都是无状态对象(即没有成员变量的对象),那么这些对象的管理会方便的多,否则,对象被使用后的状态重置工作就要由管理者来承担对象池模式经常用在频繁创建、销毁对象(并且对象创建、销毁开销很大)的场景,比如数据库连接池、线程池、任务队列池等。
示例的整体类图:
package com.alibaba.design.objectpoolpattern;/** * @author zhouyanxiang * @create 2020-07-2020/7/31-19:14 */public interface Connection { Object get(); void set(Object x);}
package com.alibaba.design.objectpoolpattern;/** * @author zhouyanxiang * @create 2020-07-2020/7/31-19:14 */public class ConnectionImplementation implements Connection { @Override public Object get() { return new Object(); } @Override public void set(Object x) { System.out.println("设置连接线程为: " + x); }}
连接池类ConnectionPool
package com.alibaba.design.objectpoolpattern;/** * @author zhouyanxiang * @create 2020-07-2020/7/31-19:15 */public class ConnectionPool { //池管理对象 private static PoolManager pool = new PoolManager(); //指定连接数 并添加 public static void addConnections(int number) { for (int i = 0; i < number; i++) { pool.add(new ConnectionImplementation()); System.out.println("添加第 " + i + " 个连接资源"); } } //获取连接 public static Connection getConnection() throws PoolManager.EmptyPoolException { return (Connection) pool.get(); } //释放指定的连接 public static void releaseConnection(Connection c) { pool.release(c); System.out.println("释放整个连接资源: " + c); }}
连接池管理类PoolManager
package com.alibaba.design.objectpoolpattern;import java.util.ArrayList;/** * @author zhouyanxiang * @create 2020-07-2020/7/31-19:12 */public class PoolManager { //连接池对象 public static class PoolItem { boolean inUse = false; Object item; //池数据 PoolItem(Object item) { this.item = item; } } //连接池集合 private ArrayList items = new ArrayList(); public void add(Object item) { this.items.add(new PoolItem(item)); } static class EmptyPoolException extends Exception { } public Object get() throws EmptyPoolException { for (int i = 0; i < items.size(); i++) { PoolItem pitem = (PoolItem) items.get(i); if (pitem.inUse == false) { pitem.inUse = true; System.out.println("获取连接资源为: " + items.get(i)); return pitem.item; } } throw new EmptyPoolException(); // return null; } /** * 释放连接 * @param item */ public void release(Object item) { for (int i = 0; i < items.size(); i++) { PoolItem pitem = (PoolItem) items.get(i); if (item == pitem.item) { pitem.inUse = false; System.out.println("释放连接资源 : " + items.get(i)); return; } } throw new RuntimeException(item + " not null"); }}
客户端测试类
package com.alibaba.design.objectpoolpattern;/** * @author zhouyanxiang * @create 2020-07-2020/7/31-19:16 */public class Test { static { ConnectionPool.addConnections(5); } public void test1() { Connection c; try { // 获得连接 c = ConnectionPool.getConnection(); } catch (PoolManager.EmptyPoolException e) { throw new RuntimeException(e); } // 设值 c.set(new Object()); //获取 c.get(); // 释放 ConnectionPool.releaseConnection(c); } public static void main(String args[]) { Test test = new Test(); test.test1(); }}
输出结果
javax.sql.PooledConnection
看它的注释
/** * An object that provides hooks for connection pool management. * APooledConnection
object * represents a physical connection to a data source. The connection * can be recycled rather than being closed when an application is * finished with it, thus reducing the number of connections that * need to be made. ** An application programmer does not use the
PooledConnection
* interface directly; rather, it is used by a middle tier infrastructure * that manages the pooling of connections. ** When an application calls the method
DataSource.getConnection
, * it gets back aConnection
object. If connection pooling is * being done, thatConnection
object is actually a handle to * aPooledConnection
object, which is a physical connection. ** The connection pool manager, typically the application server, maintains * a pool of
PooledConnection
objects. If there is a *PooledConnection
object available in the pool, the * connection pool manager returns aConnection
object that * is a handle to that physical connection. * If noPooledConnection
object is available, the * connection pool manager calls theConnectionPoolDataSource
* methodgetPoolConnection
to create a new physical connection. The * JDBC driver implementingConnectionPoolDataSource
creates a * newPooledConnection
object and returns a handle to it. ** When an application closes a connection, it calls the
Connection
* methodclose
. When connection pooling is being done, * the connection pool manager is notified because it has registered itself as * aConnectionEventListener
object using the *ConnectionPool
methodaddConnectionEventListener
. * The connection pool manager deactivates the handle to * thePooledConnection
object and returns the *PooledConnection
object to the pool of connections so that * it can be used again. Thus, when an application closes its connection, * the underlying physical connection is recycled rather than being closed. ** The physical connection is not closed until the connection pool manager * calls the
PooledConnection
methodclose
. * This method is generally called to have an orderly shutdown of the server or * if a fatal error has made the connection unusable. * ** A connection pool manager is often also a statement pool manager, maintaining * a pool of
PreparedStatement
objects. * When an application closes a prepared statement, it calls the *PreparedStatement
* methodclose
. WhenStatement
pooling is being done, * the pool manager is notified because it has registered itself as * aStatementEventListener
object using the *ConnectionPool
methodaddStatementEventListener
. * Thus, when an application closes itsPreparedStatement
, * the underlying prepared statement is recycled rather than being closed. ** * @since 1.4 */
/ ** *提供用于连接池管理的挂钩的对象。 * PooledConnection 对象 *表示与数据源的物理连接。连接 *可以回收而不是在应用程序关闭时关闭 *完成后,减少了连接数 *必须填写。 * *应用程序程序员不使用 PooledConnection *直接接口;而是由中间层基础架构使用 *管理连接池。 *
*当应用程序调用方法 DataSource.getConnection 时, *它返回一个 Connection 对象。如果连接池是 *完成后,该 Connection 对象实际上是 * PooledConnection 对象,它是物理连接。 *
*连接池管理器(通常是应用程序服务器)维护 * PooledConnection 对象的池。如果有 * PooledConnection 对象在池中可用, *连接池管理器返回一个 Connection 对象,该对象 *是该物理连接的句柄。 *如果没有 PooledConnection 对象可用,则 *连接池管理器调用 ConnectionPoolDataSource *方法 getPoolConnection 创建一个新的物理连接。的 *实现 ConnectionPoolDataSource 的JDBC驱动程序创建了一个 *新的 PooledConnection 对象并返回其句柄。 *
*当应用程序关闭连接时,它会调用 Connection *方法 close 。完成连接池后, *连接池管理器已被通知,因为它已将自己注册为 *使用 ConnectionEventListener 对象 * ConnectionPool 方法 addConnectionEventListener 。 *连接池管理器取消激活 * PooledConnection 对象并返回 * PooledConnection 对象到连接池,以便 *它可以再次使用。因此,当应用程序关闭其连接时, *基础物理连接被回收而不是关闭。 *
*直到连接池管理器才关闭物理连接 *调用 PooledConnection 方法 close 。 *此方法通常被称为有序关闭服务器或 *如果发生致命错误使连接无法使用。 * *
*连接池管理器通常也是语句池管理器,用于维护 * PreparedStatement 对象的池。 *当应用程序关闭准备好的语句时,它将调用 * PreparedStatement *方法 close 。完成 Statement 池后, *通知池管理器,因为它已将自己注册为 *使用 StatementEventListener 对象 * ConnectionPool 方法 addStatementEventListener 。 *因此,当应用程序关闭其 PreparedStatement 时, *基础的准备好的语句被回收而不是关闭。 *
* * @从1.4开始 * /
优点
复用池中对象,没有分配内存和创建堆中对象的开销, 没有释放内存和销毁堆中对象的开销, 进而减少垃圾收集器的负担, 避免内存抖动;不必重复初始化对象状态, 对于比较耗时的constructor和finalize来说非常合适;
缺点
Java的对象分配操作不比c语言的malloc调用慢, 对于轻中量级的对象, 分配/释放对象的开销可以忽略不计;
并发环境中, 多个线程可能(同时)需要获取池中对象, 进而需要在堆数据结构上进行同步或者因为锁竞争而产生阻塞, 这种开销要比创建销毁对象的开销高数百倍;
由于池中对象的数量有限, 势必成为一个可伸缩性瓶颈;
很难正确的设定对象池的大小, 如果太小则起不到作用, 如果过大, 则占用内存资源高, 可以起一个线程定期扫描分析, 将池压缩到一个合适的尺寸以节约内存,但为了获得不错的分析结果, 在扫描期间可能需要暂停复用以避免干扰(造成效率低下), 或者使用非常复杂的算法策略(增加维护难度);
设计和使用对象池容易出错, 设计上需要注意状态同步, 这是个难点, 使用上可能存在忘记归还(就像c语言编程忘记free一样), 重复归还(可能需要做个循环判断一下是否池中存在此对象, 这也是个开销), 归还后仍旧使用对象(可能造成多个线程并发使用一个对象的情况)等问题
转载地址:http://qeurn.baihongyu.com/