对象池和线程池

实际开发中,并发和多线程的情景处处存在。今天我们来聊聊对象池和线程池这个两个日常开发用的比较多,但是又很容易混淆的两个概念。

对象池

首先我们来说说对象池。对象池利用了串行封闭的概念:将对象O“借给”一个请求线程T1,T1使用完之后再交还给对象池,并保证“未擅自发布该对象”且“以后不再使用”;对象池收回对象O之后,等T2来借的时候再把他借给T2,完成对象所用权的传递。

下面是一个简化版的对象池

public abstract class AbstractObjectPool<T> {
    protected final int min;
    protected final int max;
    protected final List<T> usings = Lists.newLinkedList();
    protected final List<T> buffer = Lists.newLinkedList();
    private volatile boolean inited = false;

    public AbstractObjectPool(int min, int max) {
        this.min = min;
        this.max = max;
        if (this.min < 0 || this.min > this.max) {
            throw new IllegalArgumentException(String.format("need 0<=min<=max<=Integer.MAX_VALUE,given min%s,max:%s", this.min, this.max));
        }
    }

    public void init() {
        for (int i = 0; i < min; i++) {
            buffer.add(newObject());
        }
        inited = true;
    }

    public void checkInited() {
        if (!inited) {
            throw new IllegalStateException("not inited");
        }
    }

    abstract protected T newObject();

    public synchronized T getObject() {
        checkInited();
        if (usings.size() == max) {
            return null;
        }
        if (buffer.size() == 0) {
            T newObject = newObject();
            usings.add(newObject);
            return newObject;
        }

        T oldObject = buffer.remove(0);
        usings.add(oldObject);
        return oldObject;
    }

    public synchronized void freeObject(T object) {
        checkInited();
        if (!usings.contains(object)) {
            throw new IllegalArgumentException(String.format("object not in using queue:%s", object));
        }
        usings.remove(usings.indexOf(object));
        buffer.add(object);
    }
}

AbstractObjectPool具有以下特性:

  • 支持设置最小最大容量
  • 对象一旦申请就不再释放,避免了GC

虽然简单,但是大可以用于一些时间敏感、资源充裕的场景。如果时间进一步敏感,可将getObject()、freeObject()改写为并发程度更高的版本,但是记得得保证安全发布安全回收;如果资源不那么充裕,可以适当增加对象的回收策略。

可以看到,一个对象池的基本行为包括:

  • 创建对象newObject()
  • 借取对象getObject()
  • 归还对象freeObject()

典型的对象池有各种连接池(如数据库连接池)、常量池等。那对象池和线程是之间有什么区别呢,线程池的核心原理是不是对象池,下面我们来追溯一下java源码。

线程池

首先摆出结论:线程池糅合了对象池模型,但是核心原理是生产者-消费者模型。

继承结构如下:

用户可以将Runnable(或Callables)实例提交给线程池,线城池会异步执行该任务,返回响应的结果(完成/返回值)。

我最喜欢的是submit(Callable<T> task)方法。我们从该方法入手,逐步深入函数栈,探究线程池的实现原理。

submit()

submit()方法在ExecutorService接口中定义,在抽象类AbstractExecutorService实现,ThreadPoolExecutor直接继承。

public abstract class AbstractExecutorService implements ExecutorService {

    ...

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
}

AbstractExecutorService #newTaskFor()创建一个RunnableFutrue类型的FutureTask。核心是execute()方法

execute()方法

execute()方法在接口Executor定义,在ThreadPoolExecutor实现。

public class ThreadPoolExecutor extends AbstractExecutorService {
    ...
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();

        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
   }
   ...
}

我们暂且忽略线程池的池化策略。关注一个最简单的场景,看看线程池中任务是怎样执行的。

核心是addWorker()方法。以第8行为例,此时,线程池中线程数未达到最小线程池的大小corePoolSize,通常可以直接在第9行返回。

addWorker()

addWorker()方法是ThreadPoolExecutor的私有方法。

public class ThreadPoolExecutor extends AbstractExecutorService {
    ....
    private boolean addWorker(Runnable firstTask, boolean core) {
        ...
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {                    
                    int rs = runStateOf(ctl.get());
                    if (rs < SHUTDOWN){                       
                        workers.add(w);                       
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
    ....
}

这里去掉了很多用于管理线程池、维护线程安全的代码。假设线程池未关闭,worker(即w,下同)添加成功,则必然将worker添加至workers中。workers是一个HashSet:

    /**
     * Set containing all worker threads in pool. Accessed only when
     * holding mainLock.
     */
    private final HashSet<Worker> workers = new HashSet<Worker>();
哪里是对象池

如果说与对象池有关,那么只有workers及相当于对象池实例代码中的using,应用了对象池模型;只不过这里的using是一直增长的,直到打到最大线程池的大小maximumPoolSize。

但是很明显,线程池并没有将线程发布出去,workers仅仅是完成using保存线程的作用。那么线程池中的任务是怎样执行的呢?跟线程池有没有关系呢?

哪里又不是对象池?

注意上面addWorker()代码中的第9、17、24行代码

  • 9行将我们提交到线程池的firstTask封装入一个worker.
  • 17行将worker加入到workers维护起来
  • 24行启动了worker中的线程t

核心就在这三行,线程池没有直接在addWorker()中启动我们传进来的firstTask,代之以启动 一个worker,最终任务必然被启动,那么我们继续看Worker是怎样启动我们这个任务的。

Worker

Worker实现了Runnable接口:

private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
    ...
    /**
     * Creates with given first task and thread from ThreadFactory.
     * @param firstTask the first task (null if none)
     */
    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }

    /** Delegates main run loop to outer runWorker  */
    public void run() {
        runWorker(this);
    }
    ...
}

为什么要将构造Worker时的参数命名为firstTask?因为当且仅当需要建立新的Worker以执行任务task时,才会调用构造函数。因此,任务task对于新Worker而言,是第一个任务firstTask。

Worker的实现非常简单:将自己作为Runable实例,构造时在内部创建并持有一个线程thread。Thread和Runable的使用大家很熟悉了,核心是Worker的run方法,它直接调用了runWorker()方法。

runWorker()

重点来了。简化如下:

public class ThreadPoolExecutor extends AbstractExecutorService {
    ...
    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                 try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }
    ...
}

我们在前面将要执行的任务赋值给firstTask,5-6行首先取出任务task,并将firstTask置为null。因为接下来要执行task,firstTask字段就没有用了。

重点是10-31行的while循环。下面分情况讨论。

case1:第一次进入循环,task不为null

case1对应前面所有假设,程序正常运行进入到这个while循环。

第一次进入循环时,task==firstTask,不为null,直接进入循环体;从而执行16行的firstTask的run()方法;
异常处理我们在这里先不考虑;最后,在finally代码块中,task被置为null,导致下一轮循环会进入case2。

case2:非第一次进入循环体,task为null

case2是更为普遍的情况,是线程池的核心。

case1中,task被置为null,使得第10行的布尔表达式执行第二部分:(task = getTask()) != null) (注:getTask()方法稍后再讲,它返回一个用户已提交的任务)。假设task得到一个已提交的任务,从而16行执行的是新获得的新任务的run()方法。后面通case1,最后task也会被置为null,以后循环都进入case2。

getTask()

case2中每次循环都要获取一个新的已提交的任务,那任务从哪来呢?简化如下

public class ThreadPoolExecutor extends AbstractExecutorService {
    ...
    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
        for (;;) {
            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }
    ...
}

我们先看简单的7-11行。

首先,workQueue是一个线程安全的BlockingQueue,大部分时候使用的实现类是LinkedBlockingQueue。

private final BlockingQueue<Runnable> workQueue;

假设timed为 false,则调用阻塞的take()方法,返回的一定不是null,从而在11行return退出,将获取到的task交给某个worker线程去执行。

workQueue中的元素从哪来呢 ?这就要回到最初的execute()方法了。

execute()
public class ThreadPoolExecutor extends AbstractExecutorService {
    ...
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();

        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
   }
   ...
}

前面以8行的参数为例,此时,线程池中的线程数未达到最小线程池的大小corePoolSize,通常这个时候直接在第10行返回。

如果不满足8行的条件,则会执行到13行的。isRunning(c)判断线程池是否未关闭,这里我们只关注未关闭的情况;接下来就会执行布尔表达式的第二部分workQueue.offer(command),尝试将任务放入到队列workQueue中。

workQueue.offer()的行为取决于线程池持有的BlockingQueue实例。
Executors.newFixedThreadPool()、Executors.newSingleThreadExecutor()创建的线程池使用LinkedBlockingQueue,而Executors.newCachedThreadPool()创建的线程池则使用SynchronousQueue。

public class Executors {
    ...
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
}

以LinkedBlockingQueue为例,创建时不配置容量,即创建为无界队列,则LinkedBlockingQueue#offer()永远返回true,从而进入execute()13-19行。

当workQueue.offer()返回true时,已经将任务command放入队列workQueue中。当未来某个时刻,某worker完成一个任务后,会通过getTask()从workerQueue在获取一个任务继续执行,直到线程池关闭,直到海枯石烂。

case2小结

可以看到,实际上,线程池的核心原理与对象池模型无关,而是生产者-消费者模型。

  • 生产者(调用submit()或者execute()方法)将任务Task放入队列
  • 消费者(worker线程)循环从任务队列中取出任务处理(执行task.run())
钩子方法

回到runWorker()方法,在执行任务的过程中,线程池保留了一些钩子方法,如beforeExecute()、afterExecute()。用户可以在实现自己的线程池时,可以通过覆写钩子方法为线程池添加功能。

总结

对象池和线程池关系不大,但是经常会混淆。线程池的实现很有意思。在追溯源码之前,我一直以为线程池就是把线程存起来,用的时候取出一个执行任务;看了源码才知道它实现如此之妙,简洁优雅效率高。源码才是最好的老师。

深入理解对象池、线程池与数据库连接池 实现一个健壮的对象池需要考虑许多细节,例如同步机制、资源回收策略、池的大小限制等。下面是一个简单的自定义对象池的设计与实现:i++) {return obj;这个对象池实现了基本的借出(borrowObject)、归还(returnObject)和销毁(invalidateObject)对象的逻辑。它使用来保证线程安全,并允许对象的复用。 阅读详情

相关推荐

线程池与对象池的融合

这是一个很简单的实现啦对象池与线程池的融合,方法的主要参数,线程池大小、对象池大小、对象的创建工厂(继承接口自己实现),执行时传入方面名称即可。(invoke方法第一个为参数为方法名,第二是可变参数(及方法参数),invokeReturn为带返回值的方法)!

浅谈线程池(中):独立线程池的作用及IO线程池

在上一篇文章中,我们简单讨论了线程池的作用,以及CLR线程池的一些特性。不过关于线程池的基本概念还没有结束,这次我们再来补充一些必要的信息,有助于我们在程序中选择合适的使用方式。 独立线程池 上次我们讨论到,在一个.NET应用程序中会有一个CLR线程池,可以使用ThreadPool类中的静态方法来使用这个线程池。我们只要使用QueueUserWorkItem方法向线程池中添加任务,线程池就会负责在...

weixin_33744854的博客 360

光通信仿真入门:手把手教你用VPI和Matlab搭建第一个联合仿真系统(含避坑指南)

本文详细介绍了如何使用VPI和Matlab搭建光通信联合仿真系统,涵盖软件配置、链路搭建、数据交互等关键环节,并提供常见问题的解决方案。特别适合初学者快速掌握光通信仿真技术,避免常见错误,提升仿真效率。

weixin_28733637的博客 377

C/C++ 线程池的作用与结构 & C++代码实现

本文介绍了线程池的作用,线程池的结构示意图,并用C++实现了一个较为完整的线程池,附代码和详细的注释

98kaiii的博客 380

云桌面如何部署?云桌面系统方案部署详细教程

云桌面作为一种比较新的技术工具,核心解决的场景是方便设计师的灵活办公,本身技术就是非常的灵活性,所以在安装部署方面青椒云也做到了十足的灵活。

2301_79113726的博客 1633

数据分析面试八股文--技能软件类

数据分析的“三板斧"指的是三个在 Python 数据分析领域广泛使用的库:NumPy,Pandas,和 Matplotlib。这三个库提供了处理和分析数据的强大工具。NumPyNumPy(Numerical Python)是一个开源 Python 库,用于支持大型多维数组和矩阵运算。此外,NumPy 还提供了大量的数学函数来操作这些数组。np.array():创建数组np.arange():返回一个有终点和起点的固定步长的排列。

zfyzfw的博客 4376

对象池、连接池、线程池

什么是对象池,什么是线程池,什么是连接池,它们3个有什么区别。 对象池技术基本原理的核心有两点:缓存和共享,即对于那些被频繁使用的对象,在使用完后,不立即将它们释放,而是将它们缓存起来,以供后续的应用程序重复使用,从而减少创建对象和释放对象的次数,进而改善应用程序的性能。事实上,由于对象池技术将对象限制在一定的数量,也有效地减少了应用程序内存上的开销。 线程池以及连接池实现的原理也是跟对象池基

zfqzpp的专栏 2286

ubuntu系统禁止/开启内核自动更新

内核自动更新会导致驱动和内核版本不对应,导致驱动无法使用,所以需要禁止自动更新内核。本文介绍ubuntu系统禁止/开启内核更新的方法。

m0_50105717的博客 5289

用好对象池模式,性能提升10倍!

总之,对象池模式是一种强大的设计模式,可以通过重用昂贵的对象显著提高应用程序性能和效率。它提供了一种管理共享资源的机制,并通过限制创建的对象数量来防止资源耗尽。如果使用得当,对象池模式可以成为提高软件应用程序的可伸缩性和可靠性的有效工具。

m0_71777195的博客 483

使用geneHapR进行基因单倍型分析(以vcf文件为例)

如下图所示,可以看到,用户需要准备的包括:基因型文件、注释文件和样本信息文件,以及后续会用的表型文件。输出的主要内容有:变异位点的可视化、单倍型网络、地理分布、表型比较和连锁不平衡热图。geneHapR的工作流程。

沉香best的博客 5940

蓝鸟四轴飞控系统C程序V3.0(基础版本)深度解读

2. 解析遥控通道数据(油门 / 横滚 / 俯仰 / 偏航),转换为标准化值(-1000~+1000);3. 封装PID初始化(PID_Init,配置KP/KI/KD、输出限幅、积分限幅);,包含姿态角(Attitude_Data.Roll/Pitch/Yaw)、角速度(Gyro_Data.X/Y/Z)、遥控值(RC_Data[0~5])、电池电压(Bat_Volt)、电机PWM值(Motor_PWM[0~3])、系统状态(Sys_State),校验和通过Check_Sum()函数计算;

搏博的专栏 343

对象池、连接池、线程池等池化技术,你真的会用吗?

池化技术是一种资源管理策略,通过预先创建和维护一组可重用的对象或者连接,减少频繁创建和销毁这些资源的开销,常见的就有对象池、数据库连接池、Http连接池、线程池等等, 分别对应一些常用的Java库: commons-pool、commons-dbcp、Apache-httpclient、TreadPool等等。- 配置复杂性: 需要合理配置池的大小、逐出策略等参数- 资源泄漏:如果对象或连接没有正确归还到池中,可能会导致资源泄漏。

猿起缘灭丶的博客 1044

VS中的C#项目怎样引入另一个项目

场景 在C#项目A中需要引用项目B。 实现 将项目B手动复制到A所在的项目目录下的同一个解决方案中,即与项目A同级的目录下。 在VS中打开项目B,打开解决方案资源管理器--右击解决方案--添加--现有项目 然后找到项目B的.csproj文件打开。 然后看到资源管理器中已经将B添加进来了 点击项目A的引用-右键-添加 将项目下的解决方...

BADAO_LIUMANG_QIZHI的博客 9319

Servlet 线程池 和 EJB 对象池

Servlet 线程池 和 EJB 对象池 2008/11/13 07:26 P.M. 转至http://hi.baidu.com/%D3%C0%CA%C0%CF%E9%BA%CD/blog/item/ca83e8876c55142dc65cc31a.html 目前所有的B/S系统应用可以分为:有状态(statefull)和无状态(stateless)两大类别。有状态是指在整个系统的处理过程中...

weixin_30512785的博客 106

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Java 数据库连接池、线程池和对象池总结

Java线程池总结 Java线程池总结

langjian2012的专栏 4301

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

内存池、线程池和对象池

内存池 C/C++下内存管理是让几乎每一个程序员头疼的问题,分配足够的内存、追踪内存的分配、在不需要的时候释放内存——这个任务相当复杂。而直接使用系统调用malloc/free、new/delete进行内存分配和释放,有以下弊端: 调用malloc/new,系统需要根据“最先

470

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

源码|从串行线程封闭到对象池、线程池

相对于线程封闭,串行线程封闭离用户的距离更近一些,简单灵活,实用性强,很容易掌握。而线程封闭更多沦为单纯的设计策略,单纯使用线程封闭的场景不多。线程池与串行线程封闭、对象池的关系不大,但经常被混为一谈;没看过源码的很难想到其实现方案,面试时也能立分高下。线程池的实现很有意思。在追源码之前,猴子一直以为线程池就是把线程存起来,用的时候取出来执行任务;看了源码才知道实现如此之妙,简洁优雅效率高。源码才是最好的老师。原文地址:源码|从串行线程封闭到对象池、线程池。

攻城狮不是猫 984

专业词汇之对象池、连接池、线程池

在绝大多数的JavaWeb的应用系统开发过程中,经常会听到或见到这样的三个专业名词:对象池、连接池、线程池。下面就这三个专业知识做一个简单的小结:        1.对象池        对象池技术通常是在服务器端开发使用的技术,使用该项技术的主要原因是减少从头创建每个对象的系统开销,提高系统性能。其主要做法是:创建一个对象池,将一定数量的对象缓存到这个对象池中,需要使用时直接从对象池

zh_winer的博客 1535

线程池的基本思想还是一种对象池的思想

线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。比如:一个应用要和网络打交道,有很多步骤需要访问网络,为了不阻塞主线程,每个步骤都创建个线程,在线程中和网络交互,用线程池就变的简单,线程池是对线程的一种封装,

杨伟乔-一个极客的世界 918

池化技术(对象池)

池化技术是一种很常见的编程技巧,目的在于提前创建如内存,对象,线程资源,降低程序运行时频繁的创建销毁带来的开销。常见的有线程池,内存池,对象池等。池化技术如何提高性能,降低系统开销在实际的应用中,程序经常需要创建对象,线程和分配内存。这些设计到系统调用,系统调用会导致程序从用户态到内核态的切换会比较耗时。有了池化技术,需要用到对应的线程,对象和内存时不需要再切换上下文之需要从相应的池中获取,也不需要销毁,用完归还即可。线程池实现的原理。

weixin_43747389的博客 1339

Java池技术-对象池、连接池、线程池

概述 一个对象的生命周期大致包括三个阶段:对象的创建、对象的使用、对象的销毁。因此对象的声明周期长度可用如下表达式表示: T=T1+T2+T3,其中T1为对象的创建时间,T2为对象的使用时间,T3为对象的销毁时间,其中T2为真正有效的使用时间,而T1,T3为对象本身开销。Java采用GC对内存进行管理,当GC开始进行“垃圾对象”回收时,系统会暂停应用程序的执行。为了改善应用程序的性能,一方面应尽量...

小超人的专栏 932

对象池模式

我们先定义一个简单的对象类Object。public:Object() {我们定义一个模拟的类,用于表示一个数据库连接。public:// 构造函数:接受连接字符串并连接数据库connect();// 析构函数:断开连接// 模拟连接数据库的过程if (!// 模拟连接延迟// 断开数据库连接// 模拟执行数据库查询} else {private:// 数据库连接字符串// 连接状态标志。

ZZL优快云2017的博客 1566

对象池的介绍与使用

1. 什么是对象池 对象池,顾名思义就是一定数量的已经创建好的对象(Object)的集合。当需要创建对象时,先在池子中获取,如果池子中没有符合条件的对象,再进行创建新对象,同样,当对象需要销毁时,不做真正的销毁,而是将其setActive(false),并存入池子中。这样就避免了大量对象的创建。 2. 对象池解决什么问题? 减少频繁创建和销毁对象带来的成本,实现对象的缓存和复用,创建对象的成本比较大,并且创建比较频繁。比如线程的创建代价比较大,于是就有了常用的线程 池。对象池(模式)是一种创建型设计模式,它

Dream_bin 1万+
上一篇: Guava使用之Table
下一篇: java线程池
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值