Object Pool Design Pattern

Intent

When objects are expensive to create and they are needed only for short periods of time it is advantageous to utilize the Object Pool pattern. The Object Pool provides a cache for instantiated objects tracking which ones are in use and which are available.

Object pool pattern is a software creational design pattern which is used in situations where the cost of initializing a class instance is very high.
Basically, an Object pool is a container which contains some amount of objects. So, when an object is taken from the pool, it is not available in the pool until it is put back.
Objects in the pool have a lifecycle: Creation, Validation and Destroy.

Advantages

It boosts the performance of the application significantly.
It is most effective in a situation where the rate of initializing a class instance is high.
It manages the connections and provides a way to reuse and share them.
It can also provide the limit for the maximum number of objects that can be created.

Applicability

Use the Object Pool pattern when
  • the objects are expensive to create (allocation cost).
  • you need a large number of short-lived objects (memory fragmentation).

Source code

Let’s take the example of the database connections. It’s obvious that opening too many connections might affect the performance for several reasons:

Creating a connection is an expensive operation.

When there are too many connections opened it takes longer to create a new one and the database server will become overloaded.
Here the object pool manages the connections and provides a way to reuse and share them. It can also limit the maximum number of objects that can be created.

Let's write sample code for Creating JDBC connection pool example.

Step 1: Let's create customer POJO that represents the data that will be read from the data source.
public class Customer {

 private int id;
 private String firstName;
 private String lastName;

 /**
  * Creates an instance of customer.
  */
 public Customer(final int id, final String firstName, final String lastName) {
   this.id = id;
   this.firstName = firstName;
   this.lastName = lastName;
 }

 public int getId() {
   return id;
 }

 public void setId(final int id) {
   this.id = id;
 }

 public String getFirstName() {
   return firstName;
 }

 public void setFirstName(final String firstName) {
   this.firstName = firstName;
 }

 public String getLastName() {
   return lastName;
 }

 public void setLastName(final String lastName) {
   this.lastName = lastName;
 }

 @Override
 public String toString() {
   return "Customer{" + "id=" + getId() + ", firstName='" + getFirstName() + '\'' + ", lastName='"
       + getLastName() + '\'' + '}';
 }

 @Override
 public boolean equals(final Object that) {
   boolean isEqual = false;
   if (this == that) {
     isEqual = true;
   } else if (that != null && getClass() == that.getClass()) {
     final Customer customer = (Customer) that;
     if (getId() == customer.getId()) {
       isEqual = true;
     }
   }
   return isEqual;
 }

 @Override
 public int hashCode() {
   return getId();
 }
}
Step 2: Create a generic ObjectPool abstract class and let the subclasses to do implementations.
public abstract class ObjectPool<T> {
 private long expirationTime;

 private Hashtable<T, Long> locked, unlocked;

 public ObjectPool() {
  expirationTime = 30000; // 30 seconds
  locked = new Hashtable<T, Long>();
  unlocked = new Hashtable<T, Long>();
 }

 protected abstract T create();

 public abstract boolean validate(T o);

 public abstract void expire(T o);

 public synchronized T checkOut() {
  long now = System.currentTimeMillis();
  T t;
  if (unlocked.size() > 0) {
   Enumeration<T> e = unlocked.keys();
   while (e.hasMoreElements()) {
    t = e.nextElement();
    if ((now - unlocked.get(t)) > expirationTime) {
     // object has expired
     unlocked.remove(t);
     expire(t);
     t = null;
    } else {
     if (validate(t)) {
      unlocked.remove(t);
      locked.put(t, now);
      return (t);
     } else {
      // object failed validation
      unlocked.remove(t);
      expire(t);
      t = null;
     }
    }
   }
  }
  // no objects available, create a new one
  t = create();
  locked.put(t, now);
  return (t);
 }

 public synchronized void checkIn(T t) {
  locked.remove(t);
  unlocked.put(t, System.currentTimeMillis());
 }
}
Step 3: Let's create a JDBCConnectionPool class to create a JDBC connection pool.
public class JDBCConnectionPool extends ObjectPool<Connection> {

   private String dsn, usr, pwd;

   public JDBCConnectionPool(String driver, String dsn, String usr, String pwd) {
     super();
     try {
       Class.forName(driver).newInstance();
     } catch (Exception e) {
       e.printStackTrace();
     }
     this.dsn = dsn;
     this.usr = usr;
     this.pwd = pwd;
   }

   @Override
   protected Connection create() {
     try {
       return (DriverManager.getConnection(dsn, usr, pwd));
     } catch (SQLException e) {
       e.printStackTrace();
       return (null);
     }
   }

   @Override
   public void expire(Connection o) {
     try {
       ((Connection) o).close();
     } catch (SQLException e) {
       e.printStackTrace();
     }
   }

   @Override
   public boolean validate(Connection o) {
     try {
       return (!((Connection) o).isClosed());
     } catch (SQLException e) {
       e.printStackTrace();
       return (false);
     }
   }
}
Step 4: Let's demonstration an Object Pool design pattern using the main method.
public class ObjectPoolDemo {

 public static final String CREATE_SCHEMA1 = "CREATE TABLE CUSTOMER1 (ID NUMBER, FNAME VARCHAR(100), "
   + "LNAME VARCHAR(100))";

 public static final String CREATE_SCHEMA2 = "CREATE TABLE CUSTOMER2 (ID NUMBER, FNAME VARCHAR(100), "
   + "LNAME VARCHAR(100))";

 public static final String DELETE_SCHEMA_SQL = "DROP TABLE CUSTOMERS";

 public static void main(String args[]) throws SQLException {

  String driver = "com.mysql.jdbc.Driver";
  // Create the ConnectionPool:
  JDBCConnectionPool pool = new JDBCConnectionPool(
    driver,
    "jdbc:mysql://localhost:3306/demo?createDatabaseIfNotExist=true",
    "root", "root");

  // Create the ConnectionPool:
  pool = new JDBCConnectionPool(
    driver,
    "jdbc:mysql://localhost:3306/demo?createDatabaseIfNotExist=true",
    "root", "root");

  // Get a connection:
  final Connection connection = pool.checkOut();
  System.out.println(connection);
  // Use the connection
  Statement statement = connection.createStatement();
  statement.execute(CREATE_SCHEMA1);
  // Return the connection:
  pool.checkIn(connection);

 }
}

Comments