javaintermediate

Object Pool Pattern

Reuse expensive objects with an object pool: thread-safe checkout, return, health validation, and eviction.

java
import java.util.concurrent.*;
import java.util.function.*;

public class ObjectPool<T> {
    private final BlockingQueue<T> pool;
    private final Supplier<T> factory;
    private final Predicate<T> validator;
    private final Consumer<T> destroyer;
    private final int maxSize;

    public ObjectPool(
        int maxSize,
        Supplier<T> factory,
        Predicate<T> validator,
        Consumer<T> destroyer
    ) {
        this.maxSize = maxSize;
        this.factory = factory;
        this.validator = validator;
        this.destroyer = destroyer;
        this.pool = new LinkedBlockingQueue<>(maxSize);

        // Pre-populate
        for (int i = 0; i < maxSize; i++) {
            pool.offer(factory.get());
        }
    }

    // Borrow object
    public T borrow(long timeoutMs) throws Exception {
        T obj = pool.poll(timeoutMs, TimeUnit.MILLISECONDS);
        if (obj == null) {
            throw new TimeoutException("Pool exhausted");
        }
        // Validate before returning
        if (!validator.test(obj)) {
            destroyer.accept(obj);
            return factory.get(); // create fresh
        }
        return obj;
    }

    // Return object
    public void release(T obj) {
        if (obj == null) return;
        if (validator.test(obj)) {
            if (!pool.offer(obj)) {
                destroyer.accept(obj); // pool full
            }
        } else {
            destroyer.accept(obj);
            pool.offer(factory.get()); // replace with fresh
        }
    }

    // Auto-closeable lease
    public Lease<T> lease(long timeoutMs) throws Exception {
        return new Lease<>(borrow(timeoutMs), this);
    }

    public int available() { return pool.size(); }

    // Lease with auto-return
    public static class Lease<T> implements AutoCloseable {
        private final T object;
        private final ObjectPool<T> pool;
        private boolean returned = false;

        Lease(T object, ObjectPool<T> pool) {
            this.object = object;
            this.pool = pool;
        }

        public T get() { return object; }

        @Override
        public void close() {
            if (!returned) {
                pool.release(object);
                returned = true;
            }
        }
    }

    // Example: pooled StringBuilder
    public static void main(String[] args) throws Exception {
        ObjectPool<StringBuilder> pool = new ObjectPool<>(
            5,
            () -> new StringBuilder(256),
            sb -> sb.length() < 10_000, // reject if too large
            sb -> {} // no cleanup needed
        );

        System.out.println("Available: " + pool.available()); // 5

        try (var lease = pool.lease(1000)) {
            StringBuilder sb = lease.get();
            sb.setLength(0);
            sb.append("Hello from pooled StringBuilder!");
            System.out.println(sb);
        } // auto-returned

        System.out.println("Available: " + pool.available()); // 5
    }
}

Use Cases

  • Reusing expensive-to-create objects
  • Database connection pooling patterns
  • Thread pool and worker management

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.