3
\$\begingroup\$

I have implemented this simple unbounded ObjectPool based on a ConcurrentLinkedQueue of WeakReferences. I have a thread-unsafe object that is pretty expensive to create, and most of the time it will be used from one or two concurrent threads, but it should support any number of threads concurrently. The object has also a large memory footprint, so keeping them in memory indefintely isn't an option.

The pool usage is simple:

ObjectPool<ExpensiveObject> pool = new ObjectPool(ExpensiveObject::new);

try (ObjectPool<ExpensiveObject>.Ref ref = pool.acquire()) {
    ExpensiveObject obj = ref.obj();
    //...
}

I've been using this implementation in production for some time now, but I have never seen any other like that. Most ObjectPool implementations I see are way more complex. I wonder if I'm missing something.

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Supplier;

public class ObjectPool<T> {
    private final ConcurrentLinkedQueue<Ref> queue = new ConcurrentLinkedQueue<>();
    private final Supplier<T> factory;
    private final int maxRetries;

    @SuppressWarnings({"unused", "FieldCanBeLocal"})
    private final List<T> strong;
    //keeping a strong reference to minPoolSize elements to avoid their collection

    public ObjectPool(Supplier<T> factory) {
        this(factory, 1, 5);
    }

    public ObjectPool(Supplier<T> factory, int minPoolSize, int maxRetries) {
        this.maxRetries = maxRetries;
        this.factory = factory;
        this.strong = initMinPool(factory, minPoolSize);
    }

    private List<T> initMinPool(Supplier<T> factory, int minPoolSize) {
        List<T> strong = new ArrayList<>();
        for (int i = 0; i < minPoolSize; i++) {
            try (Ref ref = new Ref(factory.get())) {
                strong.add(ref.obj());
            }
        }
        return strong;
    }

    public Ref acquire() {
        Ref ref = null;

        for (int i = 0; i < maxRetries; i++) {
            ref = queue.poll();

            //either empty pool or valid deref'd object
            if (ref == null || ref.materialize()) break;

            ref = null;
        }

        if (ref == null)
            ref = new Ref(factory.get());

        return ref;
    }

    public class Ref implements Closeable {
        private final WeakReference<T> ref;
        private T obj;

        private Ref(T obj) {
            this.ref = new WeakReference<>(obj);
            this.obj = obj;
        }

        private boolean materialize() {
            this.obj = ref.get();
            return this.obj != null;
        }

        public T obj() {
            return obj;
        }

        @Override
        public void close() {
            if (obj == null) return;
            obj = null;
            queue.offer(this);
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ Elegant 😊 I can see why you are proud of this. IMHO a few more comments would be appropriate as this is quite complicated. \$\endgroup\$ – OldCurmudgeon Apr 28 '18 at 22:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.