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.
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.
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.
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.