generic-object-pool is a lightweight generic object pool, providing object lifecycle management, metrics, claim / release mechanism and object invalidation, as well as auto initialize a core pool and auto expiry policies.
Maven Dependency Setup
<dependency>
<groupId>com.github.bbottema</groupId>
<artifactId>generic-object-pool</artifactId>
<version>2.2.1</version>
</dependency>
// basic pool with no eager loading and no expiry policy
PoolConfig<Foo> poolConfig = PoolConfig.<Foo>builder()
.maxPoolsize(10)
.build();
GenericObjectPool<Foo> pool = new SimpleObjectPool<>(poolConfig, new MyFooAllocator());
// more advanced pool with eager loading and auto expiry
PoolConfig<Foo> poolConfig = PoolConfig.<AtomicReference<Integer>>builder()
.corePoolsize(20) // keeps 20 objects eagerly allocated at all times
.maxPoolsize(20)
// deallocate after 30 seconds, but every time an object is claimed the expiry timeout is reset
.expirationPolicy(new TimeoutSinceLastAllocationExpirationPolicy<Foo>(30, TimeUnit.SECONDS))
.build();
GenericObjectPool<Foo> pool = new SimpleObjectPool<>(poolConfig, new MyFooAllocator());
Claiming objects from the pool (blocking):
// borrow an object and block until available
PoolableObject<Foo> obj = pool.claim();
Claiming objects from the pool (blocking until timeout):
PoolableObject<Foo> obj = pool.claim(key, 1, TimeUnit.SECONDS); // null if timed out
Releasing Objects back to the Pool:
PoolableObject<Foo> obj = pool.claim();
obj.release(); // make available for reuse
// or
obj.invalidate(); // remove from pool, deallocating
Future<?> shutdownSequence = pool.shutdown();
// wait for shutdown to complete
shutdownSequence.get();
// until timeout
shutdownSequence.get(10, TimeUnit.SECONDS);
Implementing a simple Allocator to create your objects when populating the pool either eagerly or lazily.
Every method except allocate
is optional:
static class FooAllocator extends Allocator<Foo> {
/**
* Initial creation and initialization.
* Called when claim comes or when pool is eagerly loading for core size.
*/
@Override
public AtomicReference<Integer> allocate() {
return new Foo();
}
}
More comprehensive life cycle management:
static class FooAllocator extends Allocator<Foo> {
/**
* Initial creation and initialization.
* Called when claim comes or when pool is eagerly loading for core size.
*/
@Override
public AtomicReference<Integer> allocate() {
return new Foo();
}
/**
* Uninitialize an instance which has been released back to the pool, until it is claimed again.
*/
@Override
protected void deallocateForReuse(Foo object) {
object.putAtRest();
}
/**
* Reinitialize an object so it is ready to be claimed.
*/
@Override
protected void allocateForReuse(Foo object) {
object.reinitialize();
}
/**
* Clean up an object no longer needed by the pool.
*/
@Override
protected void deallocate(Foo object) {
object.clear();
}
}
PoolMetrics metrics = pool.getPoolMetrics();
metrics.getCurrentlyClaimed(); // currently claimed by threads and not released yet
metrics.getCurrentlyWaitingCount(); // currently waiting threads that want to claim
metrics.getCorePoolsize(); // number of instances to auto allocated (eager loading)
metrics.getMaxPoolsize(); // max number of objects allowed at all times
metrics.getCurrentlyAllocated(); // available + claimed objects
metrics.getTotalAllocated(); // total number of allocations during pool's existence
metrics.getTotalClaimed(); // total number of claims during pool's existence
If for some reason you need to have more control over how threads are created, you can provide you own ThreadFactory:
PoolConfig<Foo> poolConfig = PoolConfig.<AtomicReference<Integer>>builder()
.threadFactory(new MyCustomThreadFactory())
.build();
You can expire objects based on age since creation or age since last allocation. For these use:
TimeoutSinceCreationExpirationPolicy
TimeoutSinceLastAllocationExpirationPolicy
You can also spread the expiry around in a bandwidth to avoid having everything expire at the same time, hogging system resources. for these use:
SpreadedTimeoutSinceCreationExpirationPolicy
SpreadedTimeoutSinceLastAllocationExpirationPolicy
You can also combine multiple expirations, by passing instances of them as a set to:
CombinedExpirationPolicies
Finally, you can extend any of these or create your own from scratch by implementing:
ExpirationPolicy
To aid you in creating your own expiry policy, you can calculate and store an expiry age on the poolable object:
poolableObject.getExpiries().put(this, calculatedAge);
Long previouslyCalculateAge = poolableObject.getExpiries().get(this);
You can always extend one the abstract classes SpreadedTimeoutExpirationPolicy
and TimeoutExpirationPolicy
, which do this for you.