Fundamentals 13 min read

Object Pool Pattern: Principles, Apache Commons Pool Implementation, and Practical Use Cases

This article explains the object pool design pattern, its working mechanism, step‑by‑step Java implementation using Apache Commons Pool, advantages and disadvantages, and real‑world scenarios such as web server connection pooling and game object management, helping developers improve performance and resource utilization.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Object Pool Pattern: Principles, Apache Commons Pool Implementation, and Practical Use Cases

Object pool pattern is a widely used design pattern that improves application performance by reusing costly-to-create objects, especially when object creation is time‑consuming and frequent.

Working Mechanism

The pattern creates a pre‑initialized pool of objects that can be borrowed and returned; if an object is available it is removed from the pool, otherwise a new one is created and added to the pool.

Code Implementation with Apache Commons Pool

We use Apache Commons Pool to implement object pooling.

1. Add Maven 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 by implementing org.apache.commons.pool2.PooledObjectFactory<T> (or extending its abstract class):

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 object health (e.g., database connections):

public class FooEvictionPolicy implements EvictionPolicy<Foo> {
    @Override
    public boolean evict(EvictionConfig config, PooledObject<Foo> underTest, int idleCount) {
        // TODO: check if the object is still usable
        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);
    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

Performance : Reduces overhead of creating and destroying objects, leading to faster execution.

Resource Management : Controls the number of expensive resources such as database connections.

Consistency : Guarantees objects are pre‑initialized to a known state before use.

Ease of Implementation : Simple to adopt and widely supported across 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 negate benefits.

Limited Flexibility : Not suitable for scenarios requiring dynamic object creation or variable pool sizes.

Thread‑Safety : Requires proper synchronization to avoid concurrency issues.

Resource Leakage : Failing to return objects can exhaust the pool.

Typical Application Scenarios

Heavyweight objects that are expensive to create, such as threads, database connections, TCP sockets, or FTP connections, benefit from pooling.

Web Server Example : Managing database connections or network sockets with a pool improves throughput and prevents resource exhaustion.

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);
        }
    }
}

In this example, ConnectionPool manages a pool of network sockets for a web server, initializing up to ten connections and reusing them.

Game Development Example : Frequently created objects such as particles, bullets, or enemies are pooled to reduce allocation overhead.

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();
        GameObject obj1 = gameObjectPool.getObject();
        // modify obj1
        gameObjectPool.returnObject(obj1);
        GameObject obj2 = gameObjectPool.getObject();
        // modify obj2
        gameObjectPool.returnObject(obj2);
    }
}

This example shows how GameObjectPool manages reusable game objects, improving performance in real‑time applications.

Summary

In summary, the object pool pattern is a powerful design technique that can significantly boost application performance and efficiency by reusing expensive objects, providing a mechanism for shared resource management, and preventing resource exhaustion when applied correctly.

Javaperformanceresource managementDesign PatternObject poolApache Commons Pool
Code Ape Tech Column
Written by

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

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.