Building a Lightweight Java Object Pool Without Commons‑Pool2
This article walks through the design and implementation of a simple, thread‑safe object pool in Java using a LinkedBlockingQueue, explains the underlying concepts such as factory pattern and queue trimming, and provides a complete code example with a test script and output analysis.
A lightweight object‑pool utility was created to replace the heavyweight commons-pool2 in simple scenarios. The pool caches objects in a thread‑safe queue, allowing reuse without the overhead of frequent allocation and garbage collection.
Design Overview
Objects are stored in a LinkedBlockingQueue, which provides built‑in thread safety.
Public methods borrow() and back() expose borrowing and returning functionality.
An optional trimQueue(int size) method can shrink the pool to a desired maximum size, preventing unbounded memory growth.
The pool does not enforce a hard size limit; callers can decide whether to limit the number of cached instances.
Implementation
package com.funtester.funpool;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Simple generic object pool.
* Uses a LinkedBlockingQueue to store objects, a factory to create new instances,
* and provides borrow, return and trimming operations.
*/
class FunPool<F> {
/** Factory that creates new objects of type F */
private final FunPooledFactory<F> factory;
/** Thread‑safe queue that holds idle objects */
private final LinkedBlockingQueue<F> pool = new LinkedBlockingQueue<>();
FunPool(FunPooledFactory<F> factory) {
this.factory = factory;
}
/** Borrow an object from the pool. If the pool is empty a new instance is created. */
F borrow() {
F obj = pool.poll();
if (obj == null) {
obj = factory.newInstance();
}
return obj;
}
/** Return an object to the pool. If the queue is full the object is discarded. */
void back(F obj) {
boolean offered = pool.offer(obj);
if (!offered) {
// pool is at capacity; let GC reclaim the object
obj = null;
}
}
/** Current number of idle objects in the pool. */
int size() {
return pool.size();
}
/** Trim the pool so that its size does not exceed the supplied limit. */
void trimQueue(int maxSize) {
while (size() > maxSize) {
pool.poll();
}
}
}Factory interface used by the pool:
package com.funtester.funpool;
/** Functional interface for creating new pool objects. */
interface FunPooledFactory<F> {
/** Create a fresh instance of type F. */
F newInstance();
}Key Concepts
Object‑pool pattern: Reusing pre‑created objects reduces allocation latency and GC pressure in high‑throughput applications.
Factory pattern: The FunPooledFactory decouples object creation from the pool, allowing any class to be pooled without modifying the pool code.
Thread safety: LinkedBlockingQueue guarantees safe concurrent poll and offer operations.
Borrow/return semantics: borrow() retrieves an idle instance or creates a new one; back() attempts to place the instance back into the queue, discarding it if the queue is already full.
Size control: trimQueue(int maxSize) can be called periodically to bound memory usage.
Example Usage
static void main(String[] args) {
// Create a pool for Demo objects using an anonymous factory implementation
FunPool<Demo> pool = new FunPool<>(new FunPooledFactory<Demo>() {
@Override
public Demo newInstance() {
return new Demo();
}
});
// First borrow – pool is empty, a new Demo is created
Demo first = pool.borrow();
System.out.println(first.hashCode());
// Borrow two more times, returning each after use
for (int i = 0; i < 2; i++) {
Demo obj = pool.borrow();
System.out.println(obj.hashCode());
pool.back(obj);
}
// Show how many idle objects remain in the pool
System.out.println("Pool size: " + pool.size());
}
/** Simple placeholder class used in the demo */
static class Demo {}Typical console output demonstrates that the first borrowed instance is distinct, while the subsequent two borrow/return cycles reuse the same object, confirming correct pooling behavior:
1528769018
878991463
878991463
Pool size: 1This minimal pool can be integrated into Java or Groovy projects where full‑featured pooling libraries are unnecessary, offering a straightforward way to improve performance and resource utilization.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
