博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式之对象池模式
阅读量:3908 次
发布时间:2019-05-23

本文共 8814 字,大约阅读时间需要 29 分钟。

对象池模式

源代码Git地址:

文章目录

(1) 概念

Object Pool,即对象池,对象被预先创建并初始化后放入对象池中,对象提供者就能利用已有的对象来处理请求,减少对象频繁创建所占用的内存空间和初始化时间,例如数据库连接对象基本上都是创建后就被放入连接池中,后续的查询请求使用的是连接池中的对象,从而加快了查询速度。类似被放入对象池中的对象还包括Socket对象、线程对象和绘图对象(GDI对象)等。

在Object Pool设计模式中,主要有两个参与者:对象池的管理者和对象池的用户,用户从管理者那里获取对象,对象池对于用户来讲是透明的,但是用户必须遵守这些对象的使用规则,使用完对象后必须归还或者关闭对象,例如数据库连接对象使用完后必须关闭,否则该对象就会被一直占用着。

对象管理者需要维护对象池,包括初始化对象池、扩充对象池的大小、重置归还对象的状态等。

对象池在被初始化时,可能只有几个对象,甚至没有对象,按需创建对象能节省资源和时间,对于响应时间要求较高的情况,可以预先创建若干个对象。

当对象池中没有对象可供使用时,管理者一般需要使用某种策略来扩充对象池,比如将对象池的大小翻倍。另外,在多线程的情况下,可以让请求资源的线程等待,直到其他线程归还了占用的对象。

一般来说,对象池中的对象在逻辑状态上是相同的,如果都是无状态对象(即没有成员变量的对象),那么这些对象的管理会方便的多,否则,对象被使用后的状态重置工作就要由管理者来承担

(2)适用场景

对象池模式经常用在频繁创建、销毁对象(并且对象创建、销毁开销很大)的场景,比如数据库连接池、线程池、任务队列池等。

(3)代码示例

示例的整体类图:

  • Connection接口
package com.alibaba.design.objectpoolpattern;/** * @author zhouyanxiang * @create 2020-07-2020/7/31-19:14 */public interface Connection {    Object get();    void set(Object x);}
  • 实现接口的连接类ConnectionImplementation
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();    }}

输出结果

(4)该模式在源码中的体现

javax.sql.PooledConnection

看它的注释

/** * An object that provides hooks for connection pool management. * A PooledConnection 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 a Connection object. If connection pooling is * being done, that Connection object is actually a handle to * a PooledConnection 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 a Connection object that * is a handle to that physical connection. * If no PooledConnection object is available, the * connection pool manager calls the ConnectionPoolDataSource * method getPoolConnection to create a new physical connection. The * JDBC driver implementing ConnectionPoolDataSource creates a * new PooledConnection object and returns a handle to it. *

* When an application closes a connection, it calls the Connection * method close. When connection pooling is being done, * the connection pool manager is notified because it has registered itself as * a ConnectionEventListener object using the * ConnectionPool method addConnectionEventListener. * The connection pool manager deactivates the handle to * the PooledConnection 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 method close. * 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 * method close. When Statement pooling is being done, * the pool manager is notified because it has registered itself as * a StatementEventListener object using the * ConnectionPool method addStatementEventListener. * Thus, when an application closes its PreparedStatement, * 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开始 * /

(5)对象池模式的优缺点

  • 优点

    复用池中对象,没有分配内存和创建堆中对象的开销, 没有释放内存和销毁堆中对象的开销, 进而减少垃圾收集器的负担, 避免内存抖动;不必重复初始化对象状态, 对于比较耗时的constructor和finalize来说非常合适;

  • 缺点

    1. Java的对象分配操作不比c语言的malloc调用慢, 对于轻中量级的对象, 分配/释放对象的开销可以忽略不计;

    2. 并发环境中, 多个线程可能(同时)需要获取池中对象, 进而需要在堆数据结构上进行同步或者因为锁竞争而产生阻塞, 这种开销要比创建销毁对象的开销高数百倍;

    3. 由于池中对象的数量有限, 势必成为一个可伸缩性瓶颈;

    4. 很难正确的设定对象池的大小, 如果太小则起不到作用, 如果过大, 则占用内存资源高, 可以起一个线程定期扫描分析, 将池压缩到一个合适的尺寸以节约内存,但为了获得不错的分析结果, 在扫描期间可能需要暂停复用以避免干扰(造成效率低下), 或者使用非常复杂的算法策略(增加维护难度);

    5. 设计和使用对象池容易出错, 设计上需要注意状态同步, 这是个难点, 使用上可能存在忘记归还(就像c语言编程忘记free一样), 重复归还(可能需要做个循环判断一下是否池中存在此对象, 这也是个开销), 归还后仍旧使用对象(可能造成多个线程并发使用一个对象的情况)等问题

转载地址:http://qeurn.baihongyu.com/

你可能感兴趣的文章
互联网时代供应链
查看>>
WPF 使用 Expression Design 画图导出及使用 Path 画图
查看>>
使用BeetleX访问redis服务
查看>>
.NET 应用如何优雅的做功能开关(Feature Flag)
查看>>
如何踢掉 sql 语句中的尾巴,我用 C# 苦思了五种办法
查看>>
从零开始实现 ASP.NET Core MVC 的插件式开发(九) - 如何启用预编译视图
查看>>
.NET应用如何优雅的实现功能定时开关
查看>>
netcore一键部署到linux服务器以服务方式后台运行
查看>>
从 3.1 到 5.0 —— OpenReservation 更新记
查看>>
还在犹豫是否迁移.NET5?这几个项目已经上线了!
查看>>
Kuma 1.0 GA发布,70多项新功能和改进
查看>>
被 C# 的 ThreadStatic 标记的静态变量,都存放在哪里了?
查看>>
ASP.NET Core使用HostingStartup增强启动操作
查看>>
结合控制台程序和K8S的CronJob完成定时任务
查看>>
2020了,最流行的密码依旧是123456
查看>>
网传不要升级.NET5的诸多原因,你赞同几个?
查看>>
利用模板化应对ERP业务模型的快速变化
查看>>
[项目更新] 集成RabbitMQ队列与EventBus总线
查看>>
尝鲜!.NET5实操之docker+k8s,这10个坑,你不得不知!
查看>>
【招聘(深圳)】TCL通讯科技控股有限公司
查看>>