简单轻量级池实现

对象池是包含指定数量的对象的容器。 从池中获取对象时,在将对象放回之前,该对象在池中不可用。 池中的对象具有生命周期:创建,验证,销毁等。池有助于更好地管理可用资源。 有许多使用示例。 特别是在应用程序服务器中,有数据源池,线程池等。在以下情况下应使用池:

  • 高频使用相同的物体
  • 对象很大,消耗很多内存
  • 对象需要很多时间进行初始化
  • 对象使用大量的IO操作(流,套接字,数据库等)
  • 对象不是线程安全的

当我为我的一个Java项目寻找一个池实现时,我发现许多人都引用了Apache Commons Pool 。 Apache Commons Pool提供了一个对象池API。 有接口ObjectPool,ObjectPoolFactory,PoolableObjectFactory和许多实现。 池提供添加,获取,删除和返回对象的方法addObject,借款对象,invalidateObject,returnObject。 PoolableObjectFactory定义池中对象的行为,并为池的操作提供各种回调。

在仔细研究实现细节之后,我发现Apache Commons Pool不是一个轻量级的实现,这对我来说是一个开销。 此外,它对许多方法都使用了旧的Java关键字sync,因此不建议使用这些方法。 Java 5引入了用于Java并发(多线程)的Executor框架。 此处最好使用Executor框架。 我决定实现一个简单且轻量级的池,我想在这里介绍它。 它只是一个Java类。 我认为如果您不需要回调和其他高级功能就足够了。 我在GitHub上创建了一个项目easy-pool

池实现基于java.util.concurrent包中的ConcurrentLinkedQueue。 ConcurrentLinkedQueue是基于链接节点的线程安全队列。 该队列按照FIFO原理(先进先出)对元素进行排序。 我对通用池的实现如下所示

import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public abstract class ObjectPool<T>
{
    private ConcurrentLinkedQueue<T> pool;

    private ScheduledExecutorService executorService;

    /**
     * Creates the pool.
     *
     * @param minIdle minimum number of objects residing in the pool
     */
    public ObjectPool(final int minIdle) {
        // initialize pool
        initialize(minIdle);
    }

    /**
     * Creates the pool.
     *
     * @param minIdle            minimum number of objects residing in the pool
     * @param maxIdle            maximum number of objects residing in the pool
     * @param validationInterval time in seconds for periodical checking of minIdle / maxIdle conditions in a separate thread.
     *                           When the number of objects is less than minIdle, missing instances will be created.
     *                           When the number of objects is greater than maxIdle, too many instances will be removed.
     */
    public ObjectPool(final int minIdle, final int maxIdle, final long validationInterval) {
        // initialize pool
        initialize(minIdle);

        // check pool conditions in a separate thread
        executorService = Executors.newSingleThreadScheduledExecutor();
        executorService.scheduleWithFixedDelay(new Runnable()
        {
            @Override
            public void run() {
                int size = pool.size();
                if (size < minIdle) {
                    int sizeToBeAdded = minIdle - size;
                    for (int i = 0; i < sizeToBeAdded; i++) {
                        pool.add(createObject());
                    }
                } else if (size > maxIdle) {
                    int sizeToBeRemoved = size - maxIdle;
                    for (int i = 0; i < sizeToBeRemoved; i++) {
                        pool.poll();
                    }
                }
            }
        }, validationInterval, validationInterval, TimeUnit.SECONDS);
    }

    /**
     * Gets the next free object from the pool. If the pool doesn't contain any objects,
     * a new object will be created and given to the caller of this method back.
     *
     * @return T borrowed object
     */
    public T borrowObject() {
        T object;
        if ((object = pool.poll()) == null) {
            object = createObject();
        }

        return object;
    }

    /**
     * Returns object back to the pool.
     *
     * @param object object to be returned
     */
    public void returnObject(T object) {
        if (object == null) {
            return;
        }

        this.pool.offer(object);
    }

    /**
     * Shutdown this pool.
     */
    public void shutdown() {
        if (executorService != null) {
            executorService.shutdown();
        }
    }

    /**
     * Creates a new object.
     *
     * @return T new object
     */
    protected abstract T createObject();

    private void initialize(final int minIdle) {
        pool = new ConcurrentLinkedQueue<T>();

        for (int i = 0; i < minIdle; i++) {
            pool.add(createObject());
        }
    }
}

抽象类ObjectPool提供了两个主要方法:roweObject从池中获取下一个空闲对象,returnObject将借入的对象返回池中。 如果池中不包含任何对象,则将创建一个新对象,并将其交还给借方方法的调用者。 对象创建在方法createObject中进行。 任何扩展抽象类ObjectPool的类都只需实现此方法,即可使用该池。 如您所见,我还利用java.util.concurrent包中的ScheduledExecutorService。 这有什么用? 您可以指定池中驻留的最小和最大对象数。 ScheduledExecutorService在单独的线程中启动特殊任务,并在指定的时间(参数validationInterval)中观察定期对象池中的最小和最大数量。 当对象数小于最小值时,将创建丢失的实例。 当对象数大于最大值时,将删除太多实例。 有时这对于平衡池中的内存消耗对象等很有用。

让我们实现测试类以显示对具体池的使用。 首先,我们需要一个类来表示池中的对象,该类模拟耗时的过程。 称为ExportingProcess的此类需要一些时间才能实例化。

public class ExportingProcess {

    private String location;

    private long processNo = 0;

    public ExportingProcess(String location, long processNo) {
        this.location = location;
        this.processNo = processNo;

        // doing some time expensive calls / tasks
        // ...

        // for-loop is just for simulation
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
        }

        System.out.println("Object with process no. " + processNo + " was created");
    }

    public String getLocation() {
        return location;
    }

    public long getProcessNo() {
        return processNo;
    }
}

第二类实现Runnable接口并模拟线程执行的某些任务。 在run方法中,我们借用一个ExportingProcess实例,然后将其返回到池中。

public class ExportingTask implements Runnable {

    private ObjectPool<ExportingProcess> pool;

    private int threadNo;

    public ExportingTask(ObjectPool<ExportingProcess> pool, int threadNo) {
        this.pool = pool;
        this.threadNo = threadNo;
    }

    public void run() {
        // get an object from the pool
        ExportingProcess exportingProcess = pool.borrowObject();

        System.out.println("Thread " + threadNo + 
                ": Object with process no. " + exportingProcess.getProcessNo() + " was borrowed");

        // do something
        // ...

        // for-loop is just for simulation
        for (int i = 0; i < 100000; i++) {
        }

        // return ExportingProcess instance back to the pool
        pool.returnObject(exportingProcess);

        System.out.println("Thread " + threadNo + 
                ": Object with process no. " + exportingProcess.getProcessNo() + " was returned");
    }
}

现在,在JUnit类TestObjectPool中,我们创建一个ExportingProcess类型的对象池。 这是通过新的ObjectPool <ExportingProcess>(4,10,5)发生的。 参数在下面的注释中描述。

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

public class TestObjectPool
{
    private ObjectPool<ExportingProcess> pool;

    private AtomicLong processNo = new AtomicLong(0);

    @Before
    public void setUp() {
        // Create a pool of objects of type ExportingProcess. Parameters:
        // 1) Minimum number of special ExportingProcess instances residing in the pool = 4
        // 2) Maximum number of special ExportingProcess instances residing in the pool = 10
        // 3) Time in seconds for periodical checking of minIdle / maxIdle conditions in a separate thread = 5.
        //    When the number of ExportingProcess instances is less than minIdle, missing instances will be created.
        //    When the number of ExportingProcess instances is greater than maxIdle, too many instances will be removed.
        //    If the validation interval is negative, no periodical checking of minIdle / maxIdle conditions
        //    in a separate thread take place. These boundaries are ignored then.
        pool = new ObjectPool<ExportingProcess>(4, 10, 5)
        {
            protected ExportingProcess createObject() {
                // create a test object which takes some time for creation
                return new ExportingProcess("/home/temp/", processNo.incrementAndGet());
            }
        };
    }

    @After
    public void tearDown() {
        pool.shutdown();
    }

    @Test
    public void testObjectPool() {
        ExecutorService executor = Executors.newFixedThreadPool(8);

        // execute 8 tasks in separate threads
        executor.execute(new ExportingTask(pool, 1));
        executor.execute(new ExportingTask(pool, 2));
        executor.execute(new ExportingTask(pool, 3));
        executor.execute(new ExportingTask(pool, 4));
        executor.execute(new ExportingTask(pool, 5));
        executor.execute(new ExportingTask(pool, 6));
        executor.execute(new ExportingTask(pool, 7));
        executor.execute(new ExportingTask(pool, 8));

        executor.shutdown();
        try {
            executor.awaitTermination(30, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

测试输出看起来像

Object with process no. 1 was created
Object with process no. 2 was created
Object with process no. 3 was created
Object with process no. 4 was created
Thread 2: Object with process no. 2 was borrowed
Thread 1: Object with process no. 1 was borrowed
Thread 2: Object with process no. 2 was returned
Thread 3: Object with process no. 3 was borrowed
Thread 4: Object with process no. 4 was borrowed
Thread 1: Object with process no. 1 was returned
Thread 4: Object with process no. 4 was returned
Thread 8: Object with process no. 4 was borrowed
Thread 5: Object with process no. 1 was borrowed
Thread 7: Object with process no. 3 was borrowed
Thread 3: Object with process no. 3 was returned
Thread 6: Object with process no. 2 was borrowed
Thread 7: Object with process no. 3 was returned
Thread 5: Object with process no. 1 was returned
Thread 8: Object with process no. 4 was returned
Thread 6: Object with process no. 2 was returned

可以看出,访问该池的第一个线程创建了驻留在该池中的最少对象。 多次运行该测试类,我们可以发现有时4个对象相互借用,并且将在池中创建一个新的5.对象。 所有测试类均可在GitHub中获得

参考:来自JCG合作伙伴 Oleg Varaksin的简单轻量级池实现 ,位于“ 软件开发思想”博客上。

翻译自: https://www.javacodegeeks.com/2013/08/simple-and-lightweight-pool-implementation.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值