I tried to integrate an external non-thread-safe library to my web project; 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.
- 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.
- 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/
The concurrent object pool can be built by the blocking queue in Java concurrent package, and ArrayBlockingQueue also supports fairness which we require. In this implementation, I use ReentrantLock to control if we can create a new object in the pool or not. As a result, in the non dynamic creation mode i.e., 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.
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.
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();
}
}
}
来源:https://stackoverflow.com/questions/15058247/java-concurrent-object-pool