Backend Development 11 min read

Implementing Process Pooling in Java with GenericObjectPool

This article explains how Java developers can use thread pools and Apache Commons Pool2's GenericObjectPool to create a reusable process pool, covering the underlying concepts, required interfaces, Maven dependencies, configuration options, and practical code examples to improve performance.

Top Architect
Top Architect
Top Architect
Implementing Process Pooling in Java with GenericObjectPool

As a Java developer, you often encounter pooling techniques such as thread pools and connection pools, which are supported by mature tools in the Java ecosystem.

Thread Pool Usage

When a thread pool is needed, Spring's ThreadPoolTaskExecutor is commonly used because it manages thread lifecycles and task state automatically.

Thread Pool Flow Diagram

Business Scenario

In the author's use case, a Java service must start a special process via the command line, use it, and then destroy it. Because the overall startup time is sensitive, the author proposes pooling the process to avoid repeated startup overhead.

Understanding GenericObjectPool

While pooling concepts are familiar, implementing a custom pool from scratch can be difficult. Apache Commons Pool2 provides GenericObjectPool to help build custom object pools.

By the way, JedisPool is implemented using this tool.

The constructor of GenericObjectPool takes three parameters, with PooledObjectFactory being mandatory:

/**
 * Creates a new {@code GenericObjectPool} that tracks and destroys
 * objects that are checked out, but never returned to the pool.
 * 
 * @param factory   The object factory to be used to create object instances
 *                 used by this pool
 * @param config    The base pool configuration to use for this pool instance.
 *                 The configuration is used by value. Subsequent changes to
 *                 the configuration object will not be reflected in the
 *                 pool.
 * @param abandonedConfig  Configuration for abandoned object identification
 *                 and removal.  The configuration is used by value.
 */
public GenericObjectPool(final PooledObjectFactory
factory,
        final GenericObjectPoolConfig
config, final AbandonedConfig abandonedConfig) {
}

The PooledObjectFactory interface defines methods for creating, activating, validating, passivating, and destroying pooled objects:

void activateObject(PooledObject
p) throws Exception;
void destroyObject(PooledObject
p) throws Exception;
PooledObject
makeObject() throws Exception;
void passivateObject(PooledObject
p) throws Exception;
boolean validateObject(PooledObject
p);

More details can be found in the GenericObjectPool API docs.

Using GenericObjectPool

First, add the Maven dependency:

<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-pool2</artifactId>
   <version>${version}</version>
</dependency>

Implement PooledObjectFactory for your specific object. In the article, the object is a custom process:

public class MyProcessFactory implements PooledObjectFactory
{
    @Override
    public void destroyObject(PooledObject
p) throws Exception {
        final MyProcess process = p.getObject();
        if (null != process) {
            // destroy process
            process.stop();
        }
    }

    @Override
    public PooledObject
makeObject() throws Exception {
        // create a new process
        MyProcess process = new MyProcess();
        process.start();
        return new DefaultPooledObject<>(process);
    }

    // other methods can be implemented as needed
}

Then build the pool instance:

PooledObjectFactory
factory = new MyProcessFactory();
GenericObjectPool
pool = new GenericObjectPool(factory);

Borrow and return objects:

// acquire a process instance
MyProcess process = pool.borrowObject();

// return the instance
pool.returnObject(process);

Advanced Configuration with GenericObjectPoolConfig

The configuration class lets you set max total, max idle, min idle, max wait time, eviction policies, etc. Example configuration that keeps four processes alive:

private GenericObjectPoolConfig
genericObjectPoolConfig() {
    final GenericObjectPoolConfig
config = new GenericObjectPoolConfig<>();
    config.setMaxTotal(20); // maximum pool size
    config.setMaxIdle(4);   // maximum idle objects
    config.setMinIdle(0);   // minimum idle objects
    config.setMaxWait(Duration.ofSeconds(5));
    config.setTimeBetweenEvictionRuns(Duration.ofMinutes(1));
    config.setMinEvictableIdleTime(Duration.ofMinutes(10));
    config.setTestOnBorrow(true);
    config.setLifo(false);
    return config;
}

Default constants defined in the class illustrate typical values:

public static final int DEFAULT_MAX_TOTAL = 8;
public static final int DEFAULT_MAX_IDLE = 8;
public static final int DEFAULT_MIN_IDLE = 0;

By adjusting these parameters, you can create a pool that matches your business requirements and achieve the intended performance improvements.

Conclusion

The article demonstrates how to build a custom process pool in Java using Apache Commons Pool2, covering dependency setup, factory implementation, pool creation, usage, and advanced configuration, ultimately reducing process startup latency and improving overall system performance.

BackendJavaPerformanceThreadPoolGenericObjectPoolpooling
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.