Object Pool Pattern: Principles, Apache Commons Pool Implementation, and Practical Use Cases
This article explains the object pool design pattern, its working mechanism, advantages and disadvantages, and provides step‑by‑step Java code using Apache Commons Pool along with real‑world examples for web servers and game development to improve performance and resource management.
Hello everyone, I'm Chen.
The object pool pattern is a widely used design pattern in software development that improves application performance and efficiency by reusing objects whose creation is costly. It is especially useful when creating new instances is time‑consuming and the creation frequency is high, or when the number of instances is limited by resources.
Work Mechanism
The object pool works by creating a pre‑initialized pool of objects that can be borrowed and returned as needed. Instead of creating a new object each time, the pool is searched for an available reusable object. If one is found, it is removed from the pool and handed to the requester; otherwise a new object is created and added to the pool.
Code Implementation of Object Pool
I use Apache Commons Pool to implement object pooling.
1. Add dependency
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.9.0</version>
</dependency>2. Example of the object to be pooled
public class Foo {
private final String username;
public Foo(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
}3. Build a factory for object creation by implementing org.apache.commons.pool2.PooledObjectFactory<T> (or extending its abstract class) to handle creation, wrapping, destruction, etc.
public class FooPoolObjectFactory extends BasePooledObjectFactory<Foo> {
@Override
public Foo create() throws Exception {
return new Foo(String.valueOf(RandomUtils.randomInt(0, 10)));
}
@Override
public PooledObject<Foo> wrap(Foo obj) {
return new DefaultPooledObject<>(obj);
}
}4. Implement an eviction policy to periodically check the health of objects and discard unusable ones. For example, database connections need heartbeat checks.
public class FooEvictionPolicy implements EvictionPolicy<Foo> {
@Override
public boolean evict(EvictionConfig config, PooledObject<Foo> underTest, int idleCount) {
// TODO: periodically verify object functionality
return true;
}
}5. Build and configure the object pool
public GenericObjectPool<Foo> fooGenericObjectPool() {
GenericObjectPoolConfig<Foo> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setEvictionPolicy(new FooEvictionPolicy());
poolConfig.setBlockWhenExhausted(true);
poolConfig.setJmxEnabled(false);
poolConfig.setMaxWaitMillis(1000 * 10);
poolConfig.setTimeBetweenEvictionRunsMillis(60 * 1000);
poolConfig.setMinEvictableIdleTimeMillis(20 * 1000);
poolConfig.setTestWhileIdle(true);
poolConfig.setTestOnReturn(true);
poolConfig.setTestOnBorrow(true);
poolConfig.setMaxTotal(3);
// abandoned object removal configuration
AbandonedConfig abandonedConfig = new AbandonedConfig();
abandonedConfig.setRemoveAbandonedOnMaintenance(true);
abandonedConfig.setRemoveAbandonedOnBorrow(true);
return new GenericObjectPool<>(new FooPoolObjectFactory(), poolConfig, abandonedConfig);
}6. Borrow and return objects
private final GenericObjectPool<Foo> fooGenericObjectPool = fooGenericObjectPool();
public Foo borrowFoo() throws Exception {
return fooGenericObjectPool.borrowObject();
}
public void returnObject(Foo foo) {
fooGenericObjectPool.returnObject(foo);
}Advantages of Object Pool
Improved Performance : Reduces overhead of object creation and destruction by reusing pre‑initialized objects.
Resource Management : Controls shared resources such as database connections or file handles, preventing exhaustion.
Consistency : Guarantees objects are in a known state before use, useful for expensive initialization.
Ease of Implementation : The pattern is well‑known and straightforward to apply in many languages.
Disadvantages of Object Pool
Increased Complexity : Adds an abstraction layer that can make code harder to understand and maintain.
Overhead : Managing the pool itself consumes resources; improper sizing can lead to high overhead.
Limited Flexibility : Fixed‑size pools may not suit scenarios requiring dynamic scaling.
Thread‑Safety : Concurrent access requires synchronization, adding extra cost.
Resource Leaks : Failure to return objects can cause leaks and eventual resource exhaustion.
Application Scenarios
Typical objects that benefit from pooling are heavyweight and expensive to create, such as threads, database connections, TCP sockets, or FTP connections.
1. Web Server Example
Web servers handle many concurrent requests; pooling database connections, sockets, etc., improves performance and scalability.
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class ConnectionPool {
private static final int MAX_POOL_SIZE = 10;
private static final int MAX_WAIT_TIME = 5000; // milliseconds
private static final int PORT_NUMBER = 8080;
private final BlockingQueue
pool;
private final ServerSocket serverSocket;
public ConnectionPool() throws Exception {
pool = new ArrayBlockingQueue<>(MAX_POOL_SIZE);
serverSocket = new ServerSocket(PORT_NUMBER);
System.out.println("Server started on port " + PORT_NUMBER);
}
public Socket getConnection() throws Exception {
Socket connection = pool.poll();
if (connection == null) {
try {
connection = serverSocket.accept();
System.out.println("New connection accepted from " + connection.getInetAddress());
} catch (SocketTimeoutException e) {
System.out.println("Timeout waiting for connection. No connection found within " + MAX_WAIT_TIME + " milliseconds.");
}
}
return connection;
}
public void returnConnection(Socket connection) {
if (pool.size() < MAX_POOL_SIZE) {
pool.offer(connection);
System.out.println("Connection returned to pool. Pool size is now " + pool.size());
} else {
try {
connection.close();
System.out.println("Connection pool is full. Discarded connection.");
} catch (Exception e) {
System.out.println("Error closing discarded connection.");
}
}
}
public static void main(String[] args) throws Exception {
ConnectionPool connectionPool = new ConnectionPool();
while (true) {
Socket connection = connectionPool.getConnection();
// Do some work with the connection
Thread.sleep(5000);
connectionPool.returnConnection(connection);
}
}
}The ConnectionPool class manages a pool of network connections, initializing up to 10 connections and listening on port 8080.
2. Game Development Example
Games often need to create and destroy many short‑lived objects (particles, bullets, enemies). Pooling these objects improves performance.
import java.util.ArrayList;
import java.util.List;
public class GameObjectPool {
class GameObject {
public void reset() {
// reset object to default state
}
}
private static final int MAX_POOL_SIZE = 10;
private final List
pool;
public GameObjectPool() {
pool = new ArrayList<>(MAX_POOL_SIZE);
for (int i = 0; i < MAX_POOL_SIZE; i++) {
pool.add(new GameObject());
}
}
public GameObject getObject() {
GameObject gameObject = pool.remove(0);
gameObject.reset();
return gameObject;
}
public void returnObject(GameObject gameObject) {
if (pool.size() < MAX_POOL_SIZE) {
pool.add(gameObject);
}
}
public static void main(String[] args) {
GameObjectPool gameObjectPool = new GameObjectPool();
// Use game objects from pool
GameObject gameObject1 = gameObjectPool.getObject();
// modify gameObject1
gameObjectPool.returnObject(gameObject1);
GameObject gameObject2 = gameObjectPool.getObject();
// modify gameObject2
gameObjectPool.returnObject(gameObject2);
}
}The GameObjectPool class manages a pool of GameObject instances, initializing a fixed size of 10 and providing methods to borrow and return objects.
Summary
In conclusion, the object pool pattern is a powerful design pattern that can significantly improve application performance and efficiency by reusing expensive objects. It offers a mechanism for managing shared resources and prevents resource exhaustion by limiting the number of created objects. When applied correctly, it becomes an effective tool for enhancing the scalability and reliability of software applications.
Final Note (Please Support)
If this article helped you, please like, view, share, or bookmark it. Your support motivates me to keep producing content.
Additionally, I have opened a knowledge community where you can get discounted courses on Spring, MyBatis, DDD micro‑services, and more. Reply with the keyword to receive a coupon.
Follow the public account "Code Monkey Technical Column" for more fan benefits and join the discussion group by replying "join group".
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.