Object Pool Design Pattern


A software design pattern that is frequently used in Java programming to maximize the utilization of objects is called the Object Pool Design Pattern. The pattern controls how items are created and destroyed in a pool.

The management of object production and destruction is done using the Object Pool Design Pattern. The concept behind the pattern is to accumulate reusable objects rather than making new ones each time one is required. For circumstances when the cost of producing new objects is significant, such as in network connections, database connections, or costly objects, Java programmers frequently employ the Object Pool Design Pattern.

Syntax of Object Pool Design

The Object Pool Design Pattern in Java has the following syntax −

  • Make a collection of fixed-size objects.

  • Initialize the pool's items.

  • Keep track of the items that are currently in the pool.

  • Check the pool for any accessible objects whenever one is required.

  • Please ensure that any available objects within the pool are retrieved by yourself without delay returning them appropriately as needed

  • However, Should there not happen to be any items currently accessible within said reservoir - we request that a new one be created post haste so as not to waste time or resources before being returned back into circulation.

Different Algorithms of Object Pool Design

The Java object pool design pattern may be used to a variety of algorithms. Here are three possible strategies, each with a unique implementation of the code −

Lazy Initialization and Synchronization

import java.util.ArrayList;
import java.util.List;

public class ObjectPool {
   private static ObjectPool instance;
   private List<Object> pool = new ArrayList<>();
   private int poolSize;

   private ObjectPool() {}
   
   public static synchronized ObjectPool getInstance(int poolSize) {
      if (instance == null) {
         instance = new ObjectPool();
         instance.poolSize = poolSize;
         for (int i = 0; i < poolSize; i++) {
            instance.pool.add(createObject());
         }
      }
      return instance;
   }

   private static Object createObject() {
      // Create and return a new object instance
      return new Object();
   }

   public synchronized Object getObject() {
      if (pool.isEmpty()) {
         return createObject();
      } else {
         return pool.remove(pool.size() - 1);
      }
   }

   public synchronized void releaseObject(Object object) {
      if (pool.size() < poolSize) {
         pool.add(object);
      }
   }
}

The technique used here emphasizes thread safety through initialization of an accordingly lazy and synchronized object pool with preset capacity limitations that are amendable to expansion. Empty pools result in safe new instance production, while non-full instances are carefully reintroduced to maintain proper operational integrity.

Eager Initialization with Concurrent Data Structures

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class ObjectPool {
   private static final int POOL_SIZE = 10;
   private static ObjectPool instance = new ObjectPool();
   private BlockingQueue<Object> pool = new LinkedBlockingQueue<>(POOL_SIZE);

   private ObjectPool() {
      for (int i = 0; i < POOL_SIZE; i++) {
         pool.offer(createObject());
      }
   }

   private static Object createObject() {
      // Create and return a new object instance
      return new Object();
   }

   public static ObjectPool getInstance() {
      return instance;
   }

   public Object getObject() throws InterruptedException {
      return pool.take();
   }

   public void releaseObject(Object object) {
      pool.offer(object);
   }
}

In this implementation, a static final instance variable is used to eagerly initialise the object pool. The underlying data structure is a LinkedBlockingQueue, which offers thread safety without the requirement for synchronisation. During initialization, objects are added to the pool, and take() is used to retrieve them from the queue when needed. When an item is released, offer() is used to requeue it.

Time Based Expiration

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class ObjectPool {
   private static final int POOL_SIZE = 10;
   private static ObjectPool instance = new ObjectPool();
   private BlockingQueue<Object> pool = new LinkedBlockingQueue<>(POOL_SIZE);

   private ObjectPool() {}

   private static Object createObject() {
      // Create and return a new object instance
      return new Object();
   }

   public static ObjectPool getInstance() {
      return instance;
   }

   public Object getObject() throws InterruptedException {
      Object object = pool.poll(100, TimeUnit.MILLISECONDS);
      if (object == null) {
         object = createObject();
      }
      return object;
   }

   public void releaseObject(Object object) {
      pool.offer(object);
   }

}

The object pool in this version employs a time-based expiry mechanism rather than having a set size. When an object is required, it is pulled from the pool using the poll() function, which waits for a set timeout before returning null if there are no objects available. Objects are generated on demand. A new object is generated and returned if none is already present. When an object is released, offer() is used to put it back to the pool. Based on a provided timeout value, the expireObjects() function is used to remove expired items from the pool.

Different Approaches of Using Object Pool Design Pattern

Java's Object Pool Design Pattern can be implemented in a variety of ways. Here are two typical methods, complete with code samples and results −

Approach 1: Simple Object Pool Design Pattern

One way of building a simple yet practical object pool includes employing an array-based approach. Here's how this method works − once produced entirely, all objects get included within the corresponding 'pool' array for future use; when required at runtime, this collection gets checked for availability of any required item(s). If possible to obtain from existing inventory in hand,' it gets returned without delay; else another fresh object is generated on demand.'

Example

public class ObjectPool {
   private static final int POOL_SIZE = 2;
   private static List<Object> pool = new ArrayList<Object>(POOL_SIZE);
    
   static {
      for(int i = 0; i < POOL_SIZE; i++) {
         pool.add(new Object());
      }
   }
    
   public static synchronized Object getObject() {
      if(pool.size() > 0) {
         return pool.remove(0);
      } else {
         return new Object();
      }
   }
    
   public static synchronized void releaseObject(Object obj) {
      pool.add(obj);
   }
}

Example

public class ObjectPoolExample {
   public static void main(String[] args) {
      Object obj1 = ObjectPool.getObject();
      Object obj2 = ObjectPool.getObject();
        
      System.out.println("Object 1: " + obj1.toString());
      System.out.println("Object 2: " + obj2.toString());
        
      ObjectPool.releaseObject(obj1);
      ObjectPool.releaseObject(obj2);
        
      Object obj3 = ObjectPool.getObject();
      Object obj4 = ObjectPool.getObject();
        
      System.out.println("Object 3: " + obj3.toString());
      System.out.println("Object 4: " + obj4.toString());
   }
}

Output

Object 1: java.lang.Object@4fca772d
Object 2: java.lang.Object@1218025c
Object 3: java.lang.Object@4fca772d
Object 4: java.lang.Object@1218025c

Approach 2: Generic Object Pool Design Pattern

Using a list as its foundation, this technique facilitates building of a standard object pool that stores objects until they have been completely produced before inclusion in the collection. Whenever access to an item becomes necessary, the system peruses the pool in search possible options that can be used. If an option proves accessible then it will suffice; but if none do, then creating another new item becomes imperative.

Example

import java.util.ArrayList;
import java.util.List;

public class ObjectPool<T> {
   private List<T> pool;
    
   public ObjectPool(List<T> pool) {
      this.pool = pool;
   }
    
   public synchronized T getObject() {
      if (pool.size() > 0) {
         return pool.remove(0);
      } else {
         return createObject();
      }
   }
    
   public synchronized void releaseObject(T obj) {
      pool.add(obj);
   }
    
   private T createObject() {
      T obj = null;
      // create object code here
      return obj;
   }
    
   public static void main(String[] args) {
      List<String> pool = new ArrayList<String>();
      pool.add("Object 1");
      pool.add("Object 2");
        
      ObjectPool<String> objectPool = new ObjectPool<String>(pool);
        
      String obj1 = objectPool.getObject();
      String obj2 = objectPool.getObject();
        
      System.out.println("Object 1: " + obj1);
      System.out.println("Object 2: " + obj2);
        
      objectPool.releaseObject(obj1);
      objectPool.releaseObject(obj2);
        
      String obj3 = objectPool.getObject();
      String obj4 = objectPool.getObject();
        
      System.out.println("Object 3: " + obj3);
      System.out.println("Object 4: " + obj4);
   }
}

Output

Object 1: Object 1
Object 2: Object 2
Object 3: Object 1
Object 4: Object 2

Best Practices for Using Class-level Lock

When the cost of producing new objects is considerable, Java programmers frequently employ the Object Pool Design Pattern. Typical use scenarios include −

Network Connections

In a Java program, network connections may be managed using the Object Pool Design Pattern. It is preferable to reuse existing connections from a pool rather than having to create new ones every time one is required. This may enhance the application's functionality while lightening the burden on the network server.

Database Connections

In a manner similar to how network connections are managed, Java applications may likewise handle database connections using the Object Pool Design Pattern. It is preferable to reuse existing connections from a pool rather than having to create new ones every time a database connection is required. This can enhance application performance while lightening the burden on the database server.

Thread Pools

Developers working with Java programs should embrace the object pool design pattern as a means of managing thread pools efficiently. Rather than recreating each required thread as needed well-strategized usage relies on reusing pre-existing ones available in a designated work group. As a result, it encourages optimal application performance by keeping overheads low for threading creation and termination processes driven by this structure's efficiencies.

Image Processing

When tackling intensive image processing tasks in a Java based program implementing the Object Pool Design Pattern is worth considering. By utilizing pre-existing objects from a dedicated pool you can speed up your apps performance while reducing overall computing requirements for photo editing tasks.

File System Operations

If you're confronted with demanding image processing duties in a Java application, it's worthwhile to take into account applying the Object Pool Design Pattern. This technique makes use of existing items from a specific pool to enhance your program's output speed, and lessens the overall computational resources required for editing photographs.

Conclusion

The Object Pool Design Pattern is a useful design pattern in Java programming for situations when creating new objects is expensive. It provides a way to control a supply of reusable objects, lowering the total cost of creating new products. A simple object pool or a generic object pool are two examples of how the Object Pool Design Pattern might be implemented. The Object Pool Design Pattern is commonly applied in Java programming to handle expensive objects like database connections and network connections. It has a different purpose while being similar to the Flyweight and Singleton Design Patterns.

Updated on: 01-Aug-2023

525 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements