📄 objectpool.java
字号:
/*
DBPool - JDBC Connection Pool Manager
Copyright (c) Giles Winstanley
*/
package snaq.util;
import java.io.*;
import java.text.*;
import java.util.*;
/**
* Base class for a pool system implementation.
* This class provides all the base functionality required and can be easily
* extended to provide pooling support for many different types of object.
* <p>New objects are retrieved on demand according to specified limits,
* and the pool can also ensure an object's validity before returning it.
* The limits which can be set for a pool include the number of items to be
* held in the pool, and the maximum number to ever be created.
* <p>The pool will try to maintain <tt>poolSize</tt> items open and ready
* for use (unless that number has not yet been created), but if expiry is
* enabled this number will reduce if the items are not used frequently.
* If <tt>maxSize</tt> is specified then the pool will never create more
* than this number of items, and another can only be obtained from the pool
* if it is handed back by another client.
* <p><tt>ObjectPool</tt> should be sub-classed to override
* the following methods:</p>
* <pre>
* protected Reusable create() throws Exception
* protected boolean isValid(final Reusable o)
* protected void destroy(final Reusable o)
* </pre>
* <p>It is recommended that the sub-class implements methods for obtaining
* and returning items within the pool, casting the objects returned by this
* class as required to the appropriate class type.
* <p>ObjectPool also support asynchronous destruction of items, which can be
* useful in circumstances where destruction of items held can take a long
* time which would delay the <tt>checkIn</tt> method. This also applies
* to the release of the pool after it's final use, which should always be
* done using either <tt>release</tt> or <tt>releaseAsync</tt>.
* @author Giles Winstanley
*/
public abstract class ObjectPool extends LogUtil
{
private String name;
private List free, used;
private int poolSize, maxSize;
private long expiryTime;
private long requests, hits;
private boolean released = false;
private boolean asyncDestroy = false;
private DateFormat dateFormat;
private Cleaner cleaner;
private static int cleanerCount = 0;
private List listeners = new ArrayList();
/**
* Creates new object pool.
* @param name pool name
* @param poolSize maximum number of pooled objects, or 0 for no limit
* @param maxSize maximum number of possible objects, or 0 for no limit
* @param expiryTime expiry time (milliseconds) for pooled object, or 0 for no expiry
*/
protected ObjectPool(String name, int poolSize, int maxSize, long expiryTime)
{
Class type = getPoolClass();
if (!List.class.isAssignableFrom(type))
throw new RuntimeException("Invalid pool class type specified: " + type.getName() + " (must implement List)");
try
{
free = (List)type.newInstance();
used = (List)type.newInstance();
}
catch (Exception e)
{
throw new RuntimeException("Unable to instantiate pool class type: " + type.getName());
}
this.name = name;
this.setParameters(poolSize, maxSize, expiryTime);
}
/**
* Creates new object pool.
* @param name pool name
* @param poolSize maximum number of pooled objects, or 0 for no limit
* @param maxSize maximum number of possible objects, or 0 for no limit
* @param expiryTime expiry time (milliseconds) for pooled object, or 0 for no expiry
*/
protected ObjectPool(String name, int poolSize, int maxSize, int expiryTime)
{
this(name, poolSize, maxSize, (long)expiryTime);
}
/**
* Checks out an item from the pool. If no free item
* is available, a new item is created unless the maximum
* number of items has been reached. If a free item
* is not valid it is removed from the pool and another
* is retrieved.
* @return item from the pool, or null if nothing available
* @exception Exception if there is an error creating a new object
*/
protected final synchronized Reusable checkOut() throws Exception
{
if (released)
throw new RuntimeException("Pool no longer valid for use");
int oldTotalConns = used.size() + free.size();
TimeWrapper tw = null;
Reusable o = null;
if (free.size() > 0)
{
// Get an object from the free list
tw = (TimeWrapper)free.remove(0);
o = (Reusable)tw.getObject();
boolean valid = isValid(o);
while (!valid && free.size() > 0)
{
destroyObject(o);
log("Removed invalid item from pool");
tw = (TimeWrapper)free.remove(free.size() - 1);
o = (Reusable)tw.getObject();
valid = isValid(o);
}
if (free.size() == 0 && !valid)
o = null;
}
boolean hit = (o != null);
// If no free connections and can create more...create new one
if (o == null)
{
if (maxSize > 0 && used.size() == maxSize)
fireMaxSizeLimitErrorEvent();
else if (used.size() < maxSize || maxSize == 0)
{
o = create();
if (!isValid(o))
throw new RuntimeException("Unable to create a valid connection");
}
}
// If a connection has been obtained/created, add it to used items collection
if (o != null)
{
used.add(o);
requests++;
if (hit)
hits++;
firePoolCheckOutEvent();
// Check for limit reaching so events can be fired.
// (Events only fired on increase of connection numbers).
int totalConns = used.size() + free.size();
if (totalConns == poolSize && totalConns > oldTotalConns)
fireMaxPoolLimitReachedEvent();
else if (totalConns == poolSize + 1 && totalConns > oldTotalConns)
fireMaxPoolLimitExceededEvent();
if (totalConns == maxSize && totalConns > oldTotalConns)
fireMaxSizeLimitReachedEvent();
}
if (debug)
{
String ratio = used.size() + "/" + (used.size() + free.size());
String hitRate = " (HitRate=" + getHitRate() + "%)";
log("Checkout - " + ratio + hitRate + (o == null ? " - null returned" : ""));
}
return o;
}
/**
* Checks out an item from the pool.
* If there is no pooled item available and the maximum number
* possible has not been reached, another is created.
* If a free item is detected as being invalid it is removed
* from the pool and the another is retrieved.
* If an item is not available and the maximum number possible
* has been reached, the method waits for the timeout period
* for one to become available by being checked in.
* @param timeout timeout value in milliseconds
* @return item from the pool, or null if nothing available within timeout period
* @exception Exception if there is an error creating a new object
*/
protected final synchronized Reusable checkOut(long timeout) throws Exception
{
long time = System.currentTimeMillis();
Reusable o = null;
o = checkOut();
while (o == null && (System.currentTimeMillis() - time < timeout))
{
try
{
if (debug)
log("No pooled items spare...waiting for up to " + timeout + "ms");
wait(timeout);
o = checkOut();
}
catch (InterruptedException e) { log(e, "Connection checkout interrupted"); }
}
return o;
}
/**
* Checks an object into the pool, and notify other
* threads that may be waiting for one.
* @param o object to check in
*/
protected final void checkIn(Reusable o)
{
if (o == null)
{
log("Attempt to return null item");
return;
}
synchronized(this)
{
firePoolCheckInEvent();
// Check if item is from this pool
if (!used.remove(o))
{
log("Attempt to return item not belonging to pool");
throw new RuntimeException("Attempt to return item not belonging to pool " + name);
}
// If pool is full destroy object, else place in pool
boolean kill = maxSize > 0 && (free.size() + used.size() >= poolSize);
kill = kill || (maxSize == 0 && free.size() >= poolSize);
if (kill)
{
destroyObject(o);
if (debug)
log("Checkin* - " + used.size() + "/" + (used.size()+free.size()));
}
else
{
try
{
// Recycle object for next use
o.recycle();
// Add object to free list
free.add(new TimeWrapper(null, o, expiryTime));
if (debug)
log("Checkin - " + used.size() + "/" + (used.size()+free.size()));
notifyAll();
}
catch (Exception e)
{
// If unable to recycle object, destroy it
destroyObject(o);
log("Unable to recycle item - destroyed");
}
}
}
}
/**
* Releases all items from the pool, and shuts the pool down.
* If any items are still checked-out, this method waits until all items have
* been checked-in before returning.
*/
public final void release() { release(false); }
/**
* Releases all items from the pool, and shuts the pool down.
* This method returns immediately; a background thread is created to perform the release.
*/
public final synchronized void releaseAsync() { releaseAsync(false); }
/**
* Forcibly releases all items from the pool, and shuts the pool down.
* If any items are still checked-out, this method forcibly destroys them
* and then returns.
*/
public final void releaseForcibly() { release(true); }
/**
* Releases all items from the pool, and shuts the pool down.
* @param forced whether to forcibly destroy items, or let them be checked-in
*/
private final void release(boolean forced)
{
// Set released flag to prevent check-out of new items
if (released)
return;
released = true;
// Destroy cleaner thread
if (cleaner != null)
{
cleaner.halt();
try { cleaner.join(); }
catch (InterruptedException ie) { ie.printStackTrace(); }
cleaner = null;
}
synchronized(this)
{
int rel = 0, failed = 0;
TimeWrapper tw = null;
Reusable o = null;
// Destroy all items still in use
if (forced)
{
Iterator iter = used.iterator();
while (iter.hasNext())
{
o = (Reusable)iter.next();
try
{
destroy(o);
rel++;
}
catch (Exception ex)
{
failed++;
log(ex, "Unable to release item in pool");
}
}
used.clear();
}
else
{
if (debug && used.size() > 0)
log("Waiting for used items to be checked-in...");
while (used.size() > 0)
{
try { wait(); }
catch (InterruptedException e) {}
}
}
// Destroy all currently free items
Iterator iter = free.iterator();
while (iter.hasNext())
{
tw = (TimeWrapper)iter.next();
o = (Reusable)tw.getObject();
try
{
destroy(o);
rel++;
}
catch (Exception ex)
{
failed++;
log(ex, "Unable to release item in pool");
}
}
free.clear();
// Destroy log reference
if (debug)
{
String s = "Released " + rel + (rel > 1 ? " items" : " item");
if (failed > 0)
s += " (failed to release " + failed + (failed > 1 ? " items)" : " item)");
log(s);
}
firePoolReleasedEvent();
listeners.clear();
super.close();
}
}
/**
* Releases all items from the pool, and shuts the pool down.
* This method returns immediately; a background thread is created to perform the release.
*/
private final void releaseAsync(final boolean forced)
{
Thread t = new Thread(new Runnable()
{
public void run() { release(forced); }
});
t.start();
}
/**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -