设计模式之对象池模式

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

对象池模式

源代码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一样), 重复归还(可能需要做个循环判断一下是否池中存在此对象, 这也是个开销), 归还后仍旧使用对象(可能造成多个线程并发使用一个对象的情况)等问题


相关文章
|
25天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
3月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
28天前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
21天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
37 1
|
2月前
|
设计模式 Java Kotlin
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
本教程详细讲解Kotlin语法,适合希望深入了解Kotlin的开发者。对于快速学习Kotlin语法,推荐查看“简洁”系列教程。本文重点介绍了构建者模式在Kotlin中的应用与改良,包括如何使用具名可选参数简化复杂对象的创建过程,以及如何在初始化代码块中对参数进行约束和校验。
25 3
|
3月前
|
设计模式 算法 安全
设计模式——模板模式
模板方法模式、钩子方法、Spring源码AbstractApplicationContext类用到的模板方法
设计模式——模板模式
|
3月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:如何提高代码的可维护性与扩展性在软件开发领域,PHP 是一种广泛使用的服务器端脚本语言。随着项目规模的扩大和复杂性的增加,保持代码的可维护性和可扩展性变得越来越重要。本文将探讨 PHP 中的设计模式,并通过实例展示如何应用这些模式来提高代码质量。
设计模式是经过验证的解决软件设计问题的方法。它们不是具体的代码,而是一种编码和设计经验的总结。在PHP开发中,合理地使用设计模式可以显著提高代码的可维护性、复用性和扩展性。本文将介绍几种常见的设计模式,包括单例模式、工厂模式和观察者模式,并通过具体的例子展示如何在PHP项目中应用这些模式。
|
3月前
|
设计模式 Java Spring
spring源码设计模式分析-代理设计模式(二)
spring源码设计模式分析-代理设计模式(二)
|
2月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
40 0
|
3月前
|
设计模式 Java
Java设计模式-工厂方法模式(4)
Java设计模式-工厂方法模式(4)