Fundamentals 12 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, drawbacks, and real‑world scenarios such as web server connection pooling and game object reuse, providing complete code examples.

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

Introduction

The object pool pattern is a widely used design pattern that improves application performance by reusing costly objects instead of creating new instances each time, especially when object creation is expensive or the number of instances is limited by resources.

Working Mechanism

An object pool maintains a pre‑initialized set of objects that can be borrowed and returned on demand; if a reusable object is available it is handed out, otherwise a new one is created and added to the pool.

Implementation with Apache Commons Pool

1. Add the Maven dependency:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.9.0</version>
</dependency>

2. Define the object to be pooled (example Foo class):

public class Foo {
    private final String username;
    public Foo(String username) { this.username = username; }
    public String getUsername() { return username; }
}

3. Create a factory by extending BasePooledObjectFactory<Foo> :

public class FooPoolObjectFactory extends BasePooledObjectFactory
{
    @Override
    public Foo create() throws Exception {
        return new Foo(String.valueOf(RandomUtils.randomInt(0, 10)));
    }
    @Override
    public PooledObject
wrap(Foo obj) {
        return new DefaultPooledObject<>(obj);
    }
}

4. Implement an eviction policy to periodically check object health:

public class FooEvictionPolicy implements EvictionPolicy
{
    @Override
    public boolean evict(EvictionConfig config, PooledObject
underTest, int idleCount) {
        // TODO: add health‑check logic
        return true;
    }
}

5. Build and configure the pool:

public GenericObjectPool
fooGenericObjectPool() {
    GenericObjectPoolConfig
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
fooGenericObjectPool = fooGenericObjectPool();
public Foo borrowFoo() throws Exception { return fooGenericObjectPool.borrowObject(); }
public void returnObject(Foo foo) { fooGenericObjectPool.returnObject(foo); }

Advantages

Improves performance by reducing creation and destruction overhead.

Provides efficient resource management for shared resources such as DB connections.

Ensures consistency by delivering pre‑initialized objects.

Relatively easy to implement and widely applicable.

Disadvantages

Introduces additional complexity and potential maintenance burden.

Pool management itself adds overhead; improper sizing can negate benefits.

Limited flexibility for highly dynamic workloads.

Requires careful thread‑safety handling.

Risk of resource leaks if objects are not returned.

Application Scenarios

Web Server Example – Managing network sockets or database connections to handle high concurrency.

import java.net.ServerSocket;
import java.net.Socket;
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;
    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(); }
            catch (SocketTimeoutException e) { System.out.println("Timeout waiting for connection."); }
        }
        return connection;
    }
    public void returnConnection(Socket connection) {
        if (pool.size() < MAX_POOL_SIZE) { pool.offer(connection); }
        else { try { connection.close(); } catch (Exception e) {} }
    }
    public static void main(String[] args) throws Exception {
        ConnectionPool cp = new ConnectionPool();
        while (true) {
            Socket conn = cp.getConnection();
            Thread.sleep(5000);
            cp.returnConnection(conn);
        }
    }
}

Game Development Example – Reusing frequently created objects such as particles or bullets.

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

public class GameObjectPool {
    class GameObject { public void reset() { /* reset 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 obj = pool.remove(0); obj.reset(); return obj; }
    public void returnObject(GameObject obj) { if (pool.size() < MAX_POOL_SIZE) pool.add(obj); }
    public static void main(String[] args) {
        GameObjectPool gp = new GameObjectPool();
        GameObject o1 = gp.getObject();
        gp.returnObject(o1);
        GameObject o2 = gp.getObject();
        gp.returnObject(o2);
    }
}

Conclusion

The object pool pattern is a powerful technique for improving scalability and reliability by reusing expensive objects, managing shared resources, and limiting creation overhead; when applied correctly it can significantly boost performance in both server‑side and game‑development contexts.

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.