简体   繁体   English

Java并发对象池?

[英]Java Concurrent Object Pool?

I tried to integrate an external non-thread-safe library to my web project; 我尝试将外部非线程安全库集成到我的Web项目中; I found out that it's too expensive to create an instance of this object for each client thread. 我发现为每个客户端线程创建此对象的实例太昂贵了。

As a result, I would like to create an object pool which has the following property. 因此,我想创建一个具有以下属性的对象池。

  1. Dynamic object creation, the objects in the pool are dynamically created instead of creating them in the constructor. 动态对象创建时,动态创建池中的对象,而不是在构造函数中创建它们。 The pool is initially empty, and when a client thread acquires a resource object, the pool can create a new resource on demand. 池最初为空,当客户端线程获取资源对象时,池可以按需创建新资源。 Once the numbers of created objects are reached the size of the pool; 一旦创建的对象的数量达到了池的大小; then the new client threads will be blocked, and wait for other thread to recycle the resource. 然后将阻止新的客户端线程,并等待其他线程回收资源。
  2. The pool should be fair, and the fairness ensures that the first thread that asks is the first thread that gets; 池应该是公平的,公平性确保第一个要求的线程是获得的第一个线程; otherwise there is probability that some threads will just wait forever. 否则有些线程可能会永远等待。

How can I do it? 我该怎么做? I will appreciate if there is a working example. 如果有一个有效的例子,我将不胜感激。

This question and solution are summarized from https://www.dbtsai.com/blog/2013/java-concurrent-dynamic-object-pool-for-non-thread-safe-objects-using-blocking-queue/ 这个问题和解决方案总结自https://www.dbtsai.com/blog/2013/java-concurrent-dynamic-object-pool-for-non-thread-safe-objects-using-blocking-queue/

The concurrent object pool can be built by the blocking queue in Java concurrent package, and ArrayBlockingQueue also supports fairness which we require. 并发对象池可以由Java并发包中的阻塞队列构建,而ArrayBlockingQueue也支持我们需要的公平性。 In this implementation, I use ReentrantLock to control if we can create a new object in the pool or not. 在此实现中,我使用ReentrantLock来控制是否可以在池中创建新对象。 As a result, in the non dynamic creation mode ie, creating all the objects in the constructor, this lock will always be locked; 因此,在非动态创建模式中,即在构造函数中创建所有对象,此锁将始终被锁定; in the dynamic creation mode, in each time, only one object can be created, so if there is another thread acquiring this object, it will get the object from pool.take() which is blocking remove, and will wait for a new available resource in the queue. 在动态创建模式中,每次只能创建一个对象,所以如果有另一个线程获取该对象,它将从pool.take()获取阻止删除的对象,并等待新的可用对象队列中的资源。

    public abstract class ResourcePool {
        private final BlockingQueue pool;
        private final ReentrantLock lock = new ReentrantLock();
        private int createdObjects = 0;
        private int size;
     
        protected ResourcePool(int size) {
            this(size, false);
        }
     
        protected ResourcePool(int size, Boolean dynamicCreation) {
            // Enable the fairness; otherwise, some threads
            // may wait forever.
            pool = new ArrayBlockingQueue<>(size, true);
            this.size = size;
            if (!dynamicCreation) {
                lock.lock();
            }
        }
     
        public Resource acquire() throws Exception {
            if (!lock.isLocked()) {
                if (lock.tryLock()) {
                    try {
                        ++createdObjects;
                        return createObject();
                    } finally {
                        if (createdObjects < size) lock.unlock();
                    }
                }
            }
            return pool.take();
        }
     
        public void recycle(Resource resource) throws Exception {
            // Will throws Exception when the queue is full,
            // but it should never happen.
            pool.add(resource);
        }
     
        public void createPool() {
            if (lock.isLocked()) {
                for (int i = 0; i < size; ++i) {
                    pool.add(createObject());
                    createdObjects++;
                }
            }
        }
     
        protected abstract Resource createObject();
    }

In the following example, there are 5 client threads simultaneously acquiring two DataTimeFormat objects in resource pool, and those client threads will do 30 computations in total. 在以下示例中,有5个客户端线程同时在资源池中获取两个DataTimeFormat对象,这些客户端线程总共将执行30次计算。

    class DataTimeFormatResourcePool extends ResourcePool<SimpleDateFormat> {
 
    DataTimeFormatResourcePool(int size, Boolean dynamicCreation) {
        super(size, dynamicCreation);
        createPool();
    }
 
    @Override
    protected SimpleDateFormat createObject() {
        return new SimpleDateFormat("yyyyMMdd");
    }
 
    public Date convert(String input) throws Exception {
        SimpleDateFormat format = acquire();
        try {
            return format.parse(input);
        } finally {
            recycle(format);
        }
    }
}
 
public class ResourcePoolExample {
    public static void main(String args[]) {
        final DataTimeFormatResourcePool pool = new DataTimeFormatResourcePool(2, true);
 
        Callable<Date> task = new Callable<Date>() {
            @Override
            public Date call() throws Exception {
                return pool.convert("20130224");
            }
        };
 
        ExecutorService exec = Executors.newFixedThreadPool(5);
        List<Future<Date>> results = new ArrayList<>();
 
        for (int i = 0; i < 30; i++) {
            results.add(exec.submit(task));
        }
        exec.shutdown();
        try {
            for (Future<Date> result : results) {
                System.out.println(result.get());
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM