ObjectPool

第七章 对象池模式(Object Pool Pattern)

1 概要

对象池模式(Object Pool Pattern)是一种创建模式(Creational Design Patterns)。对象池模式能够提升对象创建的性能。特别是当对象创建过程比较耗时时,事先将对象创建出来,缓存在对象池中。当需要时,直接从对象池中取出对象。在使用完毕后,将对象返还给对象池。因此,在使用时,无需重新创建新对象,节省对象创建的时间。

对象池模式的一个经典应用是数据库连接池应用。因为与数据库建立连接的过程非常复杂,较为耗时。使用数据库连接池技术,能大大提高数据库应用程序的性能。

2 对象池模式的结构

在对象模式中有着3个参与方。

  1. CachedObject表示的是可放在对象池缓存的对象。
  2. CachedObjectPool是对象缓存池,用于存放闲置的对象。
  3. Client是CachedObject的使用者。它向CachedObjectPool申请对象。

图一 对象池模式结构

图一 对象池模式结构。

3 对象池模式示例

我们使用一个简单的例子来介绍对象池模式的使用方法。首先,我们定义一个CachedObject类,用于表示存放在对象池中的对象。

public class CachedObject {
}

类CachedObjectPool表示的是对象池。在构造函数中,创建一些对象,放在对象池中。所有的对象由一个链表保存维护。参数capacity表示的是初始缓存对象的个数。get()和release()方法分别从池中获得对象和将对象返回给对象池。

public class CachedObjectPool {
    private List<CachedObject> cache = null;
    public CachedObjectPool(int capacity) {
        cache = new LinkedList<CachedObject>(capacity);
        for (int i = 0; i < capacity; i++) {
            cache.add(new CachedObject());
        }
    }

    public CachedObject get() {
        return cache.remove(0);
    }

    public void release(CachedObject obj) {
        cache.add(obj);
    }
}

最后,当开发人员需要使用对象时,只需要调用get()方法从对象池中获取即可。

public class CachedObjectPoolExample {
    public static void main(String[] args) {
        CachedObjectPool objectPool = new CachedObjectPool(5);
        CachedObject obj = objectPool.get();
        ...
    }
}

4 应用举例

在Java应用程序中,常用的JDBC数据库连接池代码库有Apache Commons DBCP2C3P0Apache Tomcat JDBCHikariCP。她们都能够提供较稳定的服务。如果读者对她们的使用方法感兴趣的话,可自行阅读其源代码。

在本小节中,我们将简要的介绍一下Java多线程程序设计中的一个重要的接口ExecutorService。在通常的情况下,开发人员会生成一个线程池,用来同时运行多个任务。线程池中的线程是共享的。开发人员可以指定线程池中线程的初始个数。在运行过程中,线程池也会根据任务量动态的调整线程的个数。更多ExecutorService接口的使用细节可参考小水滴的文章"线程池(Thread Pool)和ExecutorService接口"

如下是一个常见的使用ExecutorService接口的例子。程序首先创建一个包含了3个线程的线程池,并执行一个Runnable对象。

public class ExecutorServiceExample {
    public static void main(String[] args) {
        // 创建一个ExecutorService对象
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 创建一个Runnable对象
        Runnable runnable = ()->{return;};

        // 一次只提交一个Runnable对象或者Callable对象
        Future future1 = executor.submit(runnable);
    }
}

在Executors类中,newFixedThreadPool()方法会创建一个ThreadPoolExecutor对象。

// OpenJDK 15
package java.util.concurrent;

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

在ThreadPoolExecutor类中,它实现了ExecutorService接口。它使用队列workQueue来维护需要完成的任务。线程对象是保存在成员变量workers中的。workers是一个HashSet对象。在线程对象创建完成后,可用于执行各项任务。在默认情况下,只有当收到任务之后,才会创建出“核心线程(Core Thread)”。但是,开发人员可以使用prestartCoreThread()或者prestartAllCoreThreads()方法覆盖默认行为。

// OpenJDK 15
package java.util.concurrent;

public class ThreadPoolExecutor extends AbstractExecutorService {
    ...
    private final BlockingQueue<Runnable> workQueue;

    private final HashSet<Worker> workers = new HashSet<>();
}

在成员变量workers中,每一个元素是一个Worker对象。Worker是ThreadPoolExecutor的内部类,用于维护线程状态和当前运行的任务。因此,它有着一个成员变量thread。

// java.util.concurrent.THreadPoolExecutor.java from OpenJDK 15
private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{
    ...
    final Thread thread;
    
    Runnable firstTask;
    ...
}

5 小结

本章介绍了对象池模式的结构和使用方法。在对象池模式下,对象会事先创建并缓存在对象池中。因此,在使用时,可以直接从对象池中申请一个未被使用的对象,从而节省了对象创建的时间。数据库连接池技术和线程池技术都是对象池技术的一种应用。

 

上一章
下一章

注册用户登陆后可留言

Copyright  2019 Little Waterdrop, LLC. All Rights Reserved.