Java Concurrent Dynamic Object Pool for non Thread Safe Objects using Blocking Queue

I tried to integrate an external non-thread-safe library to my web project; I found out that it’s too expensive to create an instance of the object for each client thread. Initially, I solved this issue by just sharing the same object across all the concurrent client threads, and synchronized this object externally.

However, the performance is still not acceptable; as a result, I designed a resource pool to hold fixed amount of the objects, and recycle them when the threads don’t need them anymore.

I want that the resource objects in the pool are dynamically created instead of creating all of them in the constructor. The pool will be initially empty, and when a client thread acquires a resource object, the pool can create a new resource on demand. Once the numbers of created objects are reached the size of the pool; then the new client threads will be blocked, and wait for other thread to recycle the resource.

Finally, the pool should be fair, and the fairness ensures that the first thread that asks is the first thread that gets; otherwise there is probability that some threads will just wait forever.

This concurrent object pool can be built by the blocking queue in Java concurrent package, and one of the implementation, ArrayBlockingQueue supports fairness which we require.

In this implementation, I use ReentrantLock to control if we can create a new object in the pool. As a result, in the non-dynamic creation mode i.e., creating all the objects in the constructor, this lock will always be locked. In the dynamic creation mode, in each time, only one object can be created, so if there is another thread acquiring this object, it will get the object from pool.take() which is blocking remove, and waits for a new available resource in the queue.

The source code of this ResourcePool can be downloaded at ResourcePool.java

public abstract class ResourcePool {
    private final BlockingQueue pool;
    private final ReentrantLock lock = new ReentrantLock();
    private int createdObjects = 0;
    private int size;

    protected ResourcePool(int size) {
        this(size, false);
    }

    protected ResourcePool(int size, Boolean dynamicCreation) {
        // Enable the fairness; otherwise, some threads
        // may wait forever.
        pool = new ArrayBlockingQueue<>(size, true);
        this.size = size;
        if (!dynamicCreation) {
            lock.lock();
        }
    }

    public Resource acquire() throws Exception {
        if (!lock.isLocked()) {
            if (lock.tryLock()) {
                try {
                    ++createdObjects;
                    return createObject();
                } finally {
                    if (createdObjects < size) lock.unlock();
                }
            }
        }
        return pool.take();
    }

    public void recycle(Resource resource) throws Exception {
        // Will throws Exception when the queue is full,
        // but it should never happen.
        pool.add(resource);
    }

    public void createPool() {
        if (lock.isLocked()) {
            for (int i = 0; i < size; ++i) {
                pool.add(createObject());
                createdObjects++;
            }
        }
    }

    protected abstract Resource createObject();
}

Note that in the acquire() method, I check lock.isLocked() first and then do lock.tryLock(). The ordering does matter, and we can not remove isLocked() due to that tryLock() seems to do the same work. The reason is that we will not unlock the ReentrantLock when createdObjects > size, which means if a thread get the lock and create a new resource of object, the thread still hold the lock such that it will still enter the synchronized region since it’s re-entrance lock. We use isLocked() to avoid this situation.

The DateFormat class is not thread-safe in Java. The Java document states that “Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.” I will use SimpleDateFormat as example to demonstrate how to use the ResourcePool class.

In the following example, there are 5 client threads simultaneously acquiring two DataTimeFormat objects in resource pool, and those client threads will do 30 computations in total. This code can be downloaded at ResourcePoolExample.java.

class DataTimeFormatResourcePool extends ResourcePool<SimpleDateFormat> {

    DataTimeFormatResourcePool(int size, Boolean dynamicCreation) {
        super(size, dynamicCreation);
        createPool();
    }

    @Override
    protected SimpleDateFormat createObject() {
        return new SimpleDateFormat("yyyyMMdd");
    }

    public Date convert(String input) throws Exception {
        SimpleDateFormat format = acquire();
        try {
            return format.parse(input);
        } finally {
            recycle(format);
        }
    }
}

public class ResourcePoolExample {
    public static void main(String args[]) {
        final DataTimeFormatResourcePool pool = new DataTimeFormatResourcePool(2, true);

        Callable<Date> task = new Callable<Date>() {
            @Override
            public Date call() throws Exception {
                return pool.convert("20130224");
            }
        };

        ExecutorService exec = Executors.newFixedThreadPool(5);
        List<Future<Date>> results = new ArrayList<>();

        for (int i = 0; i < 30; i++) {
            results.add(exec.submit(task));
        }
        exec.shutdown();
        try {
            for (Future<Date> result : results) {
                System.out.println(result.get());
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

About DB Tsai

DB Tsai (蔡東邦), is a Machine Learning Engineer working at Alpine Data Labs. His current focus is on Big Data, Data Mining, and Machine Learning. He uses Hadoop, Spark, Mahout, and several Machine Learning algorithms to build powerful, scalable, and robust cloud-driven applications. His favorite programming languages are Java, Scala, and Python. DB is a Ph.D. candidate in Applied Physics at Stanford University (currently taking leave of absence). He holds a Master’s degree in Electrical Engineering from Stanford University, as well as a Master's degree in Physics from National Taiwan University.

3 thoughts on “Java Concurrent Dynamic Object Pool for non Thread Safe Objects using Blocking Queue

  1. Ashwin Jayaprakash

    Avoid the use of try-locks in the fast path – acquire().

    Pools are best if you pre-create all the resources upfront. Otherwise create a separate class that does on-demand creation. Combining the 2 will slow down the code for pools where lazy creation is not required.

    Also, if the createObject() throws an exception your pool will be empty but the createdObjects counter will show = size.

  2. DB Tsai Post author

    Good opinion! However, in the code, I provide an option to disable the lazy creation by locking the lock. Don’t know will the single if statement slow down the code.

    Secondly, yeah, you are right. The createdObjects should throw the exception to handle this situation. Thanks.

  3. manoj

    1. Performance

    Object pooling provides better application performance As object creation is not done when client actually need it to perform some operation on it Instead objects are already created in the pool and readily available to perform any time. So Object creation activity is done much before So it does help in achieving better run-time performance

    2. Object sharing :

    Object Pooling encourage the concept of sharing. Objects available in pool can be shared among multiple worker threads . One thread Use the Object and once used it returns back to its Object pool and then it can be used by some other worker thread. So once created objects are not destroyed and thus destruction and creation again and again is not required. That again help in generating better performing code.

    3. Control on Object instances :
    By declaring size of Object pool we can control the no of instance creation. Thus a finite no of objects are created as decided depending upon required application capacity and scalability or peak load.

    4. Memory conservation :
    Finite no of instances are created So it helps in better memory management . Too many instances are not

    Read through more details on this here :
    http://efectivejava.blogspot.in/2013/09/8-reasons-why-object-pooling-is.html

Leave a Reply

Your email address will not be published. Required fields are marked *

* Copy This Password *

* Type Or Paste Password Here *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>