【设计模式】-创建型模式-第2章第5讲-【对象池模式】

简介: 【设计模式】-创建型模式-第2章第5讲-【对象池模式】

目录

1、对象池模式的定义

  • 1.1、先来看看百度百科的定义
  • 1.2、 对象池模式就是单例模式加享元模式

2、为什么要用对象池模式

  • 2.1、原因
  • 2.2、解决方案

3、对象池模式示例代码

4、对象池模式的应用场景

5、对象池模式的优缺点

  • 5.1、优点
  • 5.2、缺点

6、结语

1、对象池模式的定义

1.1、先来看看百度百科的定义

对象池模式 (The Object Pool Pattern) 是单例模式的一个变种,它提供了获取一系列相同对象实例的入口。当你需要对象来代表一组可替代资源的时候就变的很有用,每个对象每次可以被一个组件使用。

1.2、 对象池模式就是单例模式加享元模式

对象池模式(Object Pool Pattern),是创建型设计模式的一种,将对象预先创建并初始化后放入对象池中,对象提供者就能利用已有的对象来处理请求,减少频繁创建对象所占用的内存空间和初始化时间。一个对象池包含一组已经初始化并且可以使用的对象,可以在有需求时创建和销毁对象。对象池的用户可以从池子中取得对象,对其进行操作处理,并在不需要时归还给池子而非直接销毁。对象池是一个特殊的工厂对象,对象池模式就是单例模式加享元模式。

对象池模式和享元模式的最大区别在于:

对象池模式中会多一个回收对象重复利用的方法。所以,对象池模式应该是享元模式更加具体的一个应用场景。相当于先将对象从对象池中借出,用完之后再还回去,以此保证有限资源的重复利用。 

了解了对象池模式的定义,接下来,咱们就该思考为什么要用对象池模式。 用对象池模式的好处是什么以及它的弊端。

2、为什么要用对象池模式

2.1、原因

我们都知道对象的实例化是最耗性能的操作之一,这在过去是个大问题,现在不用再过分关注它。但是当我们处理封装外部资源的对象(例如数据库连接)时,对象的创建操作则会耗费很多资源。

2.2、解决方案

解决方案就是重用和共享这些创建成本高昂的对象,这就是我们讲到的对象池模式。先来看看它的类图结构,如下图 2-1。

7c3ebdc7bf084907b9ba4cb172b61026.png


图 2-1


对象池模式中使用的类如下所示:

ResourcePool (资源池类):用于封装逻辑的类。用来保存和管理资源列表。
Resource(资源类):用于封装特定资源的类。资源类通常被资源池类引用,因此只要资源池不重新分配,它们就永远不会被回收。
Client(客户端类):使用资源的类。

当客户需要新资源时,会向资源池类申请,资源池类检查后获取第一个可用资源并将其返回给客户端。

客户端使用完资源后会进行资源释放,资源会重新回到资源池以便重复使用。

3、对象池模式示例代码

    ObjectPool.java 文件代码如下:

    package com.zhaoyanfei.designpattern.objectPoolPattern;
     
    import java.util.Enumeration;
    import java.util.Vector;
     
    /**
     * 对象池
     * @Date 2022年10月4日
     * @author zhaoYanFei
     *
     */
    public class ObjectPool {
     
        private int numObjects = 10; // 对象池的大小 
        private int maxObjects = 50; // 对象池最大的大小 
        private Vector<PooledObject> objects = null; //存放对象池中对象的向量(PooledObject类型) 
        
        public ObjectPool() {
            
        }
        
        /*** 创建一个对象池***/ 
        public synchronized void createPool(){ 
            // 确保对象池没有创建。如果创建了,保存对象的向量 objects 不会为空 
            if (objects != null) { 
                return; // 如果己经创建,则返回 
            }
            // 创建保存对象的向量 , 初始时有 0 个元素 
            objects = new Vector<PooledObject>(); 
            // 根据 numObjects 中设置的值,循环创建指定数目的对象 
            for (int x = 0; x < numObjects; x++) {
                if ((objects.size() == 0)&&this.objects.size() <this.maxObjects) { 
                    Object obj = new Object(); 
                    objects.addElement(new PooledObject(obj)); 
                }
            }
        }
        /**
         * 获取对象
         * @return
         */
        public synchronized Object getObject(){
            // 确保对象池己被创建 
            if (objects == null) { 
                return null; // 对象池还没创建,则返回 null 
            }
            Object conn = getFreeObject(); // 获得一个可用的对象 
            // 如果目前没有可以使用的对象,即所有的对象都在使用中 
            while (conn == null) {
                wait(250); 
                conn = getFreeObject(); // 重新再试,直到获得可用的对象,如果 
                // getFreeObject() 返回的为 null,则表明创建一批对象后也不可获得可用对象 
            } 
            return conn;// 返回获得的可用的对象 
        }
        /** 
        * 本函数从对象池对象 objects 中返回一个可用的的对象 
        * 如果当前没有可用的对象,则创建几个对象,并放入对象池中。 
        * 如果创建后,所有的对象都在使用中,则返回 null 
        */ 
        private Object getFreeObject(){
            // 从对象池中获得一个可用的对象
            Object obj = findFreeObject();
            if (obj == null) {
                createPool();//如果目前对象池中没有可用的对象,创建一些对象 
                // 重新从池中查找是否有可用对象
                obj = findFreeObject();
                // 如果创建对象后仍获得不到可用的对象,则返回 null
                if (obj == null) {
                    return null;
                }
            }
            return obj;
        }
        /** 
        * 查找对象池中所有的对象,查找一个可用的对象, 
        * 如果没有可用的对象,返回 null 
        */ 
        private Object findFreeObject(){
            Object obj = null;
            PooledObject pObj = null;
            // 获得对象池向量中所有的对象 
            Enumeration enumerate = objects.elements();
            // 遍历所有的对象,看是否有可用的对象
            while (enumerate.hasMoreElements()) {
                pObj = (PooledObject) enumerate.nextElement(); 
                // 如果此对象不忙,则获得它的对象并把它设为忙 
                if (!pObj.isBusy()) { 
                    obj = pObj.getObject(); 
                    pObj.setBusy(true); 
                }
            }
            return obj;// 返回找到到的可用对象 
        }
        /** 
        * 此函数返回一个对象到对象池中,并把此对象置为空闲。 
        * 所有使用对象池获得的对象均应在不使用此对象时返回它。 
        */ 
        public void returnObject(Object obj) { 
            // 确保对象池存在,如果对象没有创建(不存在),直接返回 
            if (objects == null) {
                return;
            }
            PooledObject pObj = null;
            Enumeration enumerate = objects.elements();
            // 遍历对象池中的所有对象,找到这个要返回的对象
            while (enumerate.hasMoreElements()) { 
                pObj = (PooledObject) enumerate.nextElement(); 
                // 先找到对象池中的要返回的对象
                if (obj == pObj.getObject()) {
                    // 找到了 , 设置此对象为空闲状态
                    pObj.setBusy(false); 
                    break;
                } 
            }
        }
        /** 
        * 关闭对象池中所有的对象,并清空对象池。
        */ 
        public synchronized void closeObjectPool() { 
            // 确保对象池存在,如果不存在,返回 
            if (objects == null) {
                return; 
            } 
            PooledObject pObj = null;
            Enumeration enumerate = objects.elements();
            while (enumerate.hasMoreElements()) {
                pObj = (PooledObject) enumerate.nextElement();
                // 如果忙,等 5 秒
                if (pObj.isBusy()) {
                    wait(5000); // 等 5 秒 
                }
                // 从对象池向量中删除它 
                objects.removeElement(pObj); 
            }
            // 置对象池为空 
            objects = null; 
        }
        /** 
        * 使程序等待给定的毫秒数 
        */ 
        private void wait(int mSeconds) { 
            try { 
                Thread.sleep(mSeconds); 
            } catch (InterruptedException e) {
                
            } 
        }
        /** 
        * 内部使用的用于保存对象池中对象的类。
        * 此类中有两个成员,一个是对象,另一个是指示此对象是否正在使用的标志 。
        */ 
        class PooledObject {
            Object objection = null;// 对象
            boolean busy = false; // 此对象是否正在使用的标志,默认没有正在使用
            // 构造函数,根据一个 Object 构告一个 PooledObject 对象
            public PooledObject(Object objection) {
                this.objection = objection;
            }
            // 返回此对象中的对象 
            public Object getObject() {
                return objection;
            }
            // 设置此对象的,对象
            public void setObject(Object objection) {
                this.objection = objection; 
            } 
            // 获得对象对象是否忙
            public boolean isBusy() {
                return busy;
            }
            // 设置对象的对象正在忙
            public void setBusy(boolean busy) {
                this.busy = busy; 
            }
        }
        
    }
ObjectPoolTest.java 对象池测试用例代码如下:
    package com.zhaoyanfei.designpattern.objectPoolPattern;
     
     
    /**
     * 对象池测试类
     * @Date 2022年10月4日
     * @author zhaoYanFei
     *
     */
    public class ObjectPoolTest {
     
        public static void main(String[] args) {
            //创建对象池
            ObjectPool objPool = new ObjectPool();
            objPool.createPool();
            //客户端请求对象池获取对象Object
            Object obj = objPool.getObject();
            /**
             * TODO
             * 此处拿到对象做一些具体的业务操作
             */
            //对象用完后归还到对象池中,供其他客户端请求复用对象
            objPool.returnObject(obj);
            //最后这个操作是关闭对象池中所有对象,并清空对象池
            objPool.closeObjectPool();
        }
        
    }

4、对象池模式的应用场景

对象池模式主要适用于以下应用场景。

(1)资源受限的场景。比如,不需要可伸缩性的环境(CPU\内存等物理资源有限),CPU性能不够强劲,内存比较紧张,垃圾收集,内存抖动会造成比较大的影响,需要提高内存管理效率, 响应性比吞吐量更为重要。

(2)在内存中数量受限的对象。

(3)创建成本高的对象,可以考虑池化。

补充:常见的使用对象池的场景有在使用Socket时的各种连接池、线程池、数据库连接池等。

5、对象池模式的优缺点

5.1、优点

复用池中对象,消除创建对象、回收对象所产生的内存开销、CPU开销,以及跨网络产生的网络开销。

5.2、缺点

(1)增加了分配 / 释放对象的开销。

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

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

(4)很难合理设定对象池的大小,如果太小,则起不到作用;如果过大,则占用内存资源过高。

6、结语

对象池模式的整体设计思想就是:

当客户需要新资源时,会向资源池类申请,资源池类检查后获取第一个可用资源并将其返回给客户端。

客户端使用完资源后会进行资源释放,资源会重新回到资源池以便重复使用。

码字不易,看完之后,感觉对您还有些帮助的话,那就动动你发财的小手, 帮小编点个赞,顺便再帮小编点个免费的关注呀,我会持续为您更新设计模式相关的内容。

你的点赞就是对小编最大的支持与认可。

如果对博文有所疑问或者指正,还望评论区留言哦^_^。

今天就到这儿吧,下期我会逐步详细讲解设计模式中的行为型模式。

下期咱们不见不散……

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

热门文章

最新文章