设计模式之对象池模式

简介: 设计模式之对象池模式

对象池模式

源代码Git地址:https://gitee.com/zyxscuec/Design-pattern.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 <code>PooledConnection</code> 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.
 * <P>
 * An application programmer does not use the <code>PooledConnection</code>
 * interface directly; rather, it is used by a middle tier infrastructure
 * that manages the pooling of connections.
 * <P>
 * When an application calls the method <code>DataSource.getConnection</code>,
 * it gets back a <code>Connection</code> object.  If connection pooling is
 * being done, that <code>Connection</code> object is actually a handle to
 * a <code>PooledConnection</code> object, which is a physical connection.
 * <P>
 * The connection pool manager, typically the application server, maintains
 * a pool of <code>PooledConnection</code> objects.  If there is a
 * <code>PooledConnection</code> object available in the pool, the
 * connection pool manager returns a <code>Connection</code> object that
 * is a handle to that physical connection.
 * If no <code>PooledConnection</code> object is available, the
 * connection pool manager calls the <code>ConnectionPoolDataSource</code>
 * method <code>getPoolConnection</code> to create a new physical connection.  The
 *  JDBC driver implementing <code>ConnectionPoolDataSource</code> creates a
 *  new <code>PooledConnection</code> object and returns a handle to it.
 * <P>
 * When an application closes a connection, it calls the <code>Connection</code>
 * method <code>close</code>. When connection pooling is being done,
 * the connection pool manager is notified because it has registered itself as
 * a <code>ConnectionEventListener</code> object using the
 * <code>ConnectionPool</code> method <code>addConnectionEventListener</code>.
 * The connection pool manager deactivates the handle to
 * the <code>PooledConnection</code> object and  returns the
 * <code>PooledConnection</code> 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.
 * <P>
 * The physical connection is not closed until the connection pool manager
 * calls the <code>PooledConnection</code> method <code>close</code>.
 * This method is generally called to have an orderly shutdown of the server or
 * if a fatal error has made the connection unusable.
 *
 * <p>
 * A connection pool manager is often also a statement pool manager, maintaining
 *  a pool of <code>PreparedStatement</code> objects.
 *  When an application closes a prepared statement, it calls the
 *  <code>PreparedStatement</code>
 * method <code>close</code>. When <code>Statement</code> pooling is being done,
 * the pool manager is notified because it has registered itself as
 * a <code>StatementEventListener</code> object using the
 * <code>ConnectionPool</code> method <code>addStatementEventListener</code>.
 *  Thus, when an application closes its  <code>PreparedStatement</code>,
 * the underlying prepared statement is recycled rather than being closed.
 * <P>
 *
 * @since 1.4
 */
/ **
 *提供用于连接池管理的挂钩的对象。
 * <code> PooledConnection </ code>对象
 *表示与数据源的物理连接。连接
 *可以回收而不是在应用程序关闭时关闭
 *完成后,减少了连接数
 *必须填写。
 * <P>
 *应用程序程序员不使用<code> PooledConnection </ code>
 *直接接口;而是由中间层基础架构使用
 *管理连接池。
 * <P>
 *当应用程序调用方法<code> DataSource.getConnection </ code>时,
 *它返回一个<code> Connection </ code>对象。如果连接池是
 *完成后,该<code> Connection </ code>对象实际上是
 * <code> PooledConnection </ code>对象,它是物理连接。
 * <P>
 *连接池管理器(通常是应用程序服务器)维护
 * <code> PooledConnection </ code>对象的池。如果有
 * <code> PooledConnection </ code>对象在池中可用,
 *连接池管理器返回一个<code> Connection </ code>对象,该对象
 *是该物理连接的句柄。
 *如果没有<code> PooledConnection </ code>对象可用,则
 *连接池管理器调用<code> ConnectionPoolDataSource </ code>
 *方法<code> getPoolConnection </ code>创建一个新的物理连接。的
 *实现<code> ConnectionPoolDataSource </ code>的JDBC驱动程序创建了一个
 *新的<code> PooledConnection </ code>对象并返回其句柄。
 * <P>
 *当应用程序关闭连接时,它会调用<code> Connection </ code>
 *方法<code> close </ code>。完成连接池后,
 *连接池管理器已被通知,因为它已将自己注册为
 *使用<code> ConnectionEventListener </ code>对象
 * <code> ConnectionPool </ code>方法<code> addConnectionEventListener </ code>。
 *连接池管理器取消激活
 * <code> PooledConnection </ code>对象并返回
 * <code> PooledConnection </ code>对象到连接池,以便
 *它可以再次使用。因此,当应用程序关闭其连接时,
 *基础物理连接被回收而不是关闭。
 * <P>
 *直到连接池管理器才关闭物理连接
 *调用<code> PooledConnection </ code>方法<code> close </ code>。
 *此方法通常被称为有序关闭服务器或
 *如果发生致命错误使连接无法使用。
 *
 * <p>
 *连接池管理器通常也是语句池管理器,用于维护
 * <code> PreparedStatement </ code>对象的池。
 *当应用程序关闭准备好的语句时,它将调用
 * <code> PreparedStatement </ code>
 *方法<code> close </ code>。完成<code> Statement </ code>池后,
 *通知池管理器,因为它已将自己注册为
 *使用<code> StatementEventListener </ code>对象
 * <code> ConnectionPool </ code>方法<code> addStatementEventListener </ code>。
 *因此,当应用程序关闭其<code> PreparedStatement </ code>时,
 *基础的准备好的语句被回收而不是关闭。
 * <P>
 *
 * @从1.4开始
 * /

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

  • 优点
    复用池中对象,没有分配内存和创建堆中对象的开销, 没有释放内存和销毁堆中对象的开销, 进而减少垃圾收集器的负担, 避免内存抖动;不必重复初始化对象状态, 对于比较耗时的constructor和finalize来说非常合适;
  • 缺点
  1. Java的对象分配操作不比c语言的malloc调用慢, 对于轻中量级的对象, 分配/释放对象的开销可以忽略不计;
  2. 并发环境中, 多个线程可能(同时)需要获取池中对象, 进而需要在堆数据结构上进行同步或者因为锁竞争而产生阻塞, 这种开销要比创建销毁对象的开销高数百倍;
  3. 由于池中对象的数量有限, 势必成为一个可伸缩性瓶颈;
  4. 很难正确的设定对象池的大小, 如果太小则起不到作用, 如果过大, 则占用内存资源高, 可以起一个线程定期扫描分析, 将池压缩到一个合适的尺寸以节约内存,但为了获得不错的分析结果, 在扫描期间可能需要暂停复用以避免干扰(造成效率低下), 或者使用非常复杂的算法策略(增加维护难度);
  5. 设计和使用对象池容易出错, 设计上需要注意状态同步, 这是个难点, 使用上可能存在忘记归还(就像c语言编程忘记free一样), 重复归还(可能需要做个循环判断一下是否池中存在此对象, 这也是个开销), 归还后仍旧使用对象(可能造成多个线程并发使用一个对象的情况)等问题


相关文章
|
19天前
|
设计模式 SQL 算法
设计模式了解哪些,模版模式
设计模式了解哪些,模版模式
21 0
|
1月前
|
设计模式 Java uml
C++设计模式之 依赖注入模式探索
C++设计模式之 依赖注入模式探索
37 0
|
16天前
|
设计模式 Java 数据库
小谈设计模式(2)—简单工厂模式
小谈设计模式(2)—简单工厂模式
|
22小时前
|
设计模式 消息中间件 Java
Java 设计模式:探索发布-订阅模式的原理与应用
【4月更文挑战第27天】发布-订阅模式是一种消息传递范式,被广泛用于构建松散耦合的系统。在 Java 中,这种模式允许多个对象监听和响应感兴趣的事件。
8 2
|
4天前
|
设计模式 存储 JavaScript
[设计模式Java实现附plantuml源码~创建型] 多态工厂的实现——工厂方法模式
[设计模式Java实现附plantuml源码~创建型] 多态工厂的实现——工厂方法模式
|
4天前
|
设计模式 Java Go
[设计模式Java实现附plantuml源码~创建型] 集中式工厂的实现~简单工厂模式
[设计模式Java实现附plantuml源码~创建型] 集中式工厂的实现~简单工厂模式
|
6天前
|
设计模式
设计模式(一)简单工厂模式
设计模式(一)简单工厂模式
13 0
|
16天前
|
设计模式 Java
小谈设计模式(9)—工厂方法模式
小谈设计模式(9)—工厂方法模式
|
1月前
|
设计模式 编译器
解析器模式--设计模式
解析器模式--设计模式
19 0
|
1月前
|
设计模式 算法
构建器模式--设计模式
构建器模式--设计模式
18 0