How to create Generic Object Pool in Java
design pattern
object pool
factory method
generic object pool
In a application you can have resources that are limited or time consuming to create a new one. e.g. a connection to a database, a new thread. A solution is to create a the limited resource once and reuse it. To address this problem the object pool pattern was introduced. The object pool design will have the mechanism to create a new object to keep the objects and to destroy the objects if necessary.
Requirements
Before the implementation we need to define some requirements for the object pool pattern:
- Managing all kind of objects;
- Support for custom object creation;
- Bound the number of resources to a limit;
- Support for pre-loading items to the pool;
- Support for concurrency and multithreading scenarios;
Implementation
This is a basic implementation, so there is enough room to improve. To describe the object pool pattern behavior will use an interface with three methods: get, release and shutdown. Will use generic type T to be used with any object.
package com.admfactory;
public interface Pool {
/*
* @return one of the pooled objects.
*/
T get();
/*
* @param object T to be return back to pool
*/
void release(T object);
/**
* Shuts down the pool. Should release all resources.
*/
void shutdown();
}
To ensure that the solution will support custom object creation will introduce other design pattern: Factory Method Factory. The Factory Method Pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. Using this pattern will be able to implement the object pool pattern with generic objects without knowing the implementation of the object. To define the Factory Method Factory will use an interface with a createNew()
method:
package com.admfactory;
public interface ObjectFactory {
/**
* Returns a new instance of an object type T.
*
* @return T an new instance of the object type T
*/
public abstract T createNew();
}
With these two interfaces that defines our API for object pool pattern, we can implement an abstract class to define the logic for object pooling.
package com.admfactory;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public abstract class ObjectPool implements ObjectFactory, Pool {
private int size;
private boolean shutdown;
private BlockingQueue objects;
public ObjectPool(int size) {
this.size = size;
shutdown = false;
init();
}
/*
* initiate the pool with fix size
*/
private void init() {
objects = new LinkedBlockingQueue();
for (int i = 0; i < size; i++) {
objects.add(createNew());
}
}
@Override
public T get() {
if (!shutdown) {
T t = null;
try {
t = objects.take();
}
catch (Exception e) {
e.printStackTrace();
}
return t;
}
throw new IllegalStateException("Object pool is already shutdown.");
}
@Override
public void release(T t) {
try {
objects.offer(t);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void shutdown() {
objects.clear();
}
public int size() {
return objects.size();
}
}
To keep all the objects will use a BlockingQueue
object. This will ensure that the object will be delivered only if is accessible, otherwise will wait until an object will become accessible.
The main bits for this implementation are:
init()
method: will create a fix size object pool object by callingcreateNew()
method and adding to the queue;get()
method: will get an object from the pool and will and will deliver it to the user;release()
method: will return an object to the pool;shutdown()
method: will close down the pool. For simplicity of this example I only remove the objects from the pool. In real life you might need to release the memory too. e.g. if is a database connection pool, you need to close the connection before remove the object from the pool.size()
method: returns the size of the pool.
Usage
To prove that this is working we need to write the concrete implementation for the pool and an object to be pooled. Let's call the object: ExpensiveResource
and the pool ExpensiveResourcePool
.
ExpensiveResource
Simple class that print something on the console.
package com.admfactory;
public class ExpensiveResource {
private String name;
public ExpensiveResource(String name) {
this.name = name;
}
public void doSomething() {
System.out.println("I am resource " + this.name + ".");
}
}
ExpensiveResourcePool
Concrete implementation for the object pool abstract class. Need to provide only the method to create a new object.
package com.admfactory;
import java.util.UUID;
public class ExpensiveResourcePool extends ObjectPool {
public ExpensiveResourcePool(int size) {
super(size);
}
@Override
public ExpensiveResource createNew() {
//return an object with random name
return new ExpensiveResource(UUID.randomUUID().toString());
}
}
Example
Here is a simple class to demonstrate the usage of the object pool.
package com.admfactory;
public class App {
public static void main(String[] args) {
final ExpensiveResourcePool pool = new ExpensiveResourcePool(3);
System.out.println("Simple usage");
System.out.println();
System.out.println("Pool size:" + pool.size());
/* simple usage - get the object */
ExpensiveResource obj0 = pool.get();
/* simple usage - use the object */
obj0.doSomething();
/* to check that the object was removed from the pool */
System.out.println("Pool size:" + pool.size());
/* simple usage - return the object */
pool.release(obj0);
System.out.println();
System.out.println("extended usage");
System.out.println();
final ExpensiveResource obj1 = pool.get();
ExpensiveResource obj2 = pool.get();
ExpensiveResource obj3 = pool.get();
/* to check that the object was removed from the pool */
System.out.println("Pool size:" + pool.size());
obj1.doSomething();
obj2.doSomething();
obj3.doSomething();
/* create a new thread to simulate the long operation for obj1 - this will avoid blocking the test app */
Runnable exec = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
pool.release(obj1);
}
};
Thread thread = new Thread(exec);
thread.start();
/* will wait until the thread will finish and will return the object to the pool - 10 sec
* will be the same object as for obj1.
*/
System.out.println("Pool size:" + pool.size());
ExpensiveResource obj4 = pool.get();
obj4.doSomething();
/*return all objects to the pool */
pool.release(obj4);
pool.release(obj2);
pool.release(obj3);
/* check the pool size */
System.out.println("Pool size:" + pool.size());
/* shutdown the pool*/
pool.shutdown();
/* check the pool size */
System.out.println("Pool size:" + pool.size());
}
}
Output
This is the output console. During the test you can notice that the print out for the obj4 object (the last "I am resource.." message) is displayed after 10 sec, after an object became available in the pool.
Simple usage
Pool size:3
I am resource 9d17d609-9f04-4502-a11c-0e5c1914c309.
Pool size:2
extended usage
Pool size:0
I am resource 1484a512-24ec-4088-af99-34fb698c12d2.
I am resource 8b3d46d2-7d98-4906-a665-cc1e3277c10b.
I am resource 9d17d609-9f04-4502-a11c-0e5c1914c309.
Pool size:0
I am resource 1484a512-24ec-4088-af99-34fb698c12d2.
Pool size:3
Pool size:0
Improvements
This implementation is very simple and was intended just to present the idea of object pool pattern.
Possible improvements
- add variable pool size - increase to max size when needed;
- add mechanism to clean the pool to min when the pool is idle;
- clean the memory on shutdown;
- keep the used objects in a separate container - in case that accidentally are not return the pool to be clean after a specific period of time. This will avoid the pool to be blocked;
- keep alive mechanism - in case that objects need to be kept in a specific state. e.g. if the pooled objects are database connections to check from time to time if the connection is still open.
- validation/clean mechanism for objects that become invalid.
...and the list it might continue. In a future post will try to address all these improvements.