Object pool pattern is a software creational design pattern that is used in situations where the cost of initializing a class instance is very high. Basically, an Object pool is a container that 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
- Destroy.
Object pooling can provide a large speed improvement; it works best when the cost of initializing a class instance is high, the pace of instantiation is high, and the number of instantiations in use at any given moment is low.
The Object Pool design is useful when objects are expensive to construct and only need to be used for a short period of time. In this design pattern, we keep track of which instantiated objects are in use and which are accessible.
How to create Multiple pools of Objects?
We all know how to create a single pool of objects. But there may be a requirement to create multiple pools of objects of variable size. This means instead of creating a single pool of objects o fixed size, we can have multiple pools & each pool has a different number of total objects.
What is the real-time requirement for it?
Memory allocation as needed for error handling or in emergent scenarios in programming can be accomplished by generating multiple variable-size memory pools.
There are a few objects for which the production of a new object appears to be slightly more expensive since they are not considered lightweight. Database connection objects, parser objects, thread generation, and so forth are examples. We may have to construct numerous such pools of objects in any application. Because the generation of such objects is expensive, it will have a negative impact on the performance of any program. We can use the same object again & again.
Implementation:
We are here creating a class “Thread” whose Objects are there in pools & another class “ThreadPool” that is used to create multiple pools of “Thread” Objects.
In our class Thread class(class 1)
We have data members:
- “uniqueId” Stores unique id generated for each thread object.
- “name” Keeps the name for the thread object.
In our class ThreadPool class(class 2)
We have data members:
- isFirst = true to keep true initially & whenever 1st Thread object in pool is created, after 1st object creation, it is set to false.
- myThreads[] of type Thread which is an array reference that will refer to the Thread Class object(Thread Pool Array)
- isUsed[] of type boolean which is an array to Keep track that which Thread’s objects are in use currently
- noOfThreadPools to keep count of no of Thread Pools & is a class level Property
- ThreadreferenceCount to keep count of no of thread references in a particular pool
- poolNum to store unique number given to every Pool Created
Example
Java
// Java Program to Creating Multiple Pools Of Objects of // Variable Size // Importing required classes import java.io.*; import java.util.*; // Class 1 // Helper class class Thread { // Members of this class private int uniqueId; private static int uniqueIDGen = 0 ; private String thread_name; // Constructor that initializes the objects of Thread // class Thread() { uniqueId = generateUniqueId(); } // Method 1 // To set the value of the data member, // name of the Thread's Object public void setNameOfThread(String name) { thread_name = name; } // Method 2 public String getNameOfThread() { return thread_name; } // Method 3 // To generate a unique id for each Thread's Object private int generateUniqueId() { return uniqueIDGen++; } // Method 4 // To Compare that the "this" reference and // "t1" reference refer to same object public boolean compare(Thread t1) { if (t1.uniqueId == this .uniqueId) { return true ; } else { return false ; } } } // Class 2 // Helper class class ThreadPool { // member variables of this class // To keep true whenever 1st object of a pool is // created, // after 1st object creation, it is set to false private boolean isFirst = true ; // Array reference of myThread (Thread Pool Array) private Thread myThreads[] = null ; // Array to Keep track that which Thread's objects // are in use currently private boolean isUsed[] = null ; private int n; // To keep count of no of Thread Pools and // is a class level Property private static int noOfThreadPools = 0 ; // Keeping count of no of thread references // in a particular pool private int ThreadreferenceCount = 0 ; // To Give a unique number to every Pool Created private int poolNum = 0 ; // Constructor of class 2 public ThreadPool() { noOfThreadPools++; poolNum = noOfThreadPools; } // Method 1 // To give "size" of each Thread pool and // creating size no of objects of Thread Type, // returning true if successful, else false public boolean initialize( int size) { boolean status = false ; if ( true == isFirst) { isFirst = false ; n = size; // Assign memory of reference of array // using new keyword myThreads = new Thread[n]; for ( int i = 0 ; i < n; i++) { // Creating new object in heap of Thread // Class & myThread[i] reference refer it myThreads[i] = new Thread(); // we are assigning thread name to each // thread String name = Integer.toString(i + 1 ); name = name.concat( " is the thread number of the pool " ); name = name.concat( Integer.toString(poolNum)); // Name assigning finishes myThreads[i].setNameOfThread(name); } // Creating new array of boolean type which is // referred by isUsed reference isUsed = new boolean [n]; for ( int i = 0 ; i < n; i++) { // Now making isUsed false for all the // references false, // false because when initializing, no // reference from outside is referring to // any of the objects created (from o to // size-1) isUsed[i] = false ; } status = true ; } return status; } // Method 2 // To get reference of the Thread Objects Present in the // Pool If no object is free, it returns null public Thread getReference() { Thread threadToReturn = null ; if (ThreadreferenceCount < n) { for ( int i = 0 ; i < n; i++) { // When no reference from outside the class // is referring to object myThread[i], // isUsed[i] is false if ( false == isUsed[i]) { threadToReturn = myThreads[i]; // Make isUsed[i] as true because we are // returning a reference to the object // referred by myThread[i] isUsed[i] = true ; // Incrementing the count because we are // returning a reference to the object // referred by myThread[i] to outside // world now this object cant be referred // by another reference from outside ThreadreferenceCount++; // Break keyword break ; } } } return threadToReturn; } // Method 3 // To release the outside world reference; // Here : "thread" - passed as parameter public void releaseReference(Thread thread) { if (ThreadreferenceCount > 0 ) { for ( int i = 0 ; i < n; i++) { // Compare the "thread" parameter with every // reference and release the matched one by // making isUsed[i] = false; if ( true == thread.compare(myThreads[i])) { // Make isUsed[i] is false to show no // reference from // outside the class is referring to // object myThread[i] isUsed[i] = false ; // Decrementing the count because we are // releasing a reference to the object // in outside world ThreadreferenceCount--; break ; } } } } // Method 4 // To make all the objects free for garbage collection // To restore to initial situation public boolean deInitialize() { boolean status = false ; if (isFirst = false ) { status = true ; } // This condition is not required if one wants to // deinitialize() even if the references are present // outside else if (ThreadreferenceCount == 0 ) { for ( int i = 0 ; i < n; i++) { myThreads[i] = null ; } myThreads = null ; isUsed = null ; n = 0 ; isFirst = true ; status = true ; } return status; } } // Class 3 // Min class public class Main { // Main driver method public static void main(String[] args) { // Display message on console for better readability System.out.println( "*****************POOL1 Of Threads Created*****************" ); // Creating object of class 2 in class 3 main() // method ThreadPool p1 = new ThreadPool(); // Creating pool of 2 objects if (p1.initialize( 2 )) { System.out.println( "Pool 1 initialized" ); } else { System.out.println( "Pool 1 not initialized" ); } // Now getting 2 references to the 2 Objects from // this pool // Thread 1 Thread t1 = p1.getReference(); if (t1 != null ) { System.out.println(t1.getNameOfThread()); } else { System.out.println( " t1 do not refer any object" ); } // Thread 2 Thread t2 = p1.getReference(); if (t2 != null ) { System.out.println(t2.getNameOfThread()); } else { System.out.println( " t2 do not refer any object" ); } // Thread 3 Thread t3 = p1.getReference(); if (t3 != null ) { System.out.println(t3.getNameOfThread()); } else { System.out.println( " t3 do not refer any object" ); } // Releasing all the references of pool 1 p1.releaseReference(t1); t1 = null ; p1.releaseReference(t2); t2 = null ; // As we have not used deInitialize() method, we // cannot initialize the pool p1 again if (p1.initialize( 2 )) { // Print statement System.out.println( "Pool 1 initialized" ); } else { // Print statement System.out.println( "Pool 1 not initialized as it was already initialized\n" ); } // Creating pool 2 // Display message on console for better readability System.out.println( "*****************POOL2 Of Threads Created*****************" ); ThreadPool p2 = new ThreadPool(); // Creating pool of 3 objects p2.initialize( 3 ); Thread tp1 = p2.getReference(); if (tp1 != null ) { System.out.println(tp1.getNameOfThread()); } else { System.out.println( "tp1 dont refer to any object" ); } // Releasing references of pool 2 p2.releaseReference(tp1); tp1 = null ; // Deinitializing both the pools // using deInitialize() method p1.deInitialize(); p2.deInitialize(); } } |
*****************POOL1 Of Threads Created***************** Pool 1 initialized 1 is the thread number of the pool 1 2 is the thread number of the pool 1 t3 do not refer any object Pool 1 not initialized as it was already initialized *****************POOL2 Of Threads Created***************** 1 is the thread number of the pool 2
Output explanation:
- Here, we create two pools of Thread Objects by:
ThreadPool p1 = new ThreadPool() & ThreadPool p2 = new ThreadPool()
- Then we called initialize() method on p1 & p2 giving size (total “Thread” Objects in the pool ) – 2 & 3 respectively.
- Then 2 references of Thread class ( t1 & t2) are created who are referencing Objects (of Thread type) in Pool p1.
- A 3rd reference(t3) of Thread class is created, and we are trying to get Object (by calling getReference() on p1)from pool p1 so that t3 can refer it. But t3 do not refer to any object as Pool p1 size is 2, only 2 references from the outside world can refer to the 2 objects in the pool.
- Then we release both the references (t1 & t2) by calling the method releaseReference(). Now the objects in the pool are available & the outside world references can refer them by calling the method getReference() on p1.
- Similarly, we created another reference (tp1) but it refers to the Thread object contained in the pool p2 as we called the method getReference() on p2.
- Then we release the references (tp1) by calling the method releaseReference().
- At the end we call method deInitialize() on p1 & p2 bring back p1 & p2 pool to the starting condition that was before calling initialize().