A pool of objects that should only be used by one thread at a time : Timer « Development Class « Java






A pool of objects that should only be used by one thread at a time

      
/*
 * ResourcePool.java Created Oct 21, 2010 by Andrew Butler, PSL
 */
//package prisms.util;

/**
 * A pool of objects that should only be used by one thread at a time
 * 
 * @param <T>
 *            The type of resource that is held by this pool
 */
public class ResourcePool<T> {
  /**
   * A ResourceCreationException may be thrown by a
   * {@link ResourcePool.ResourceCreator} if it is unable to create a new
   * resource
   */
  public static class ResourceCreationException extends Exception {
    /**
     * @param message
     *            The message detailing why the resource cannot be created
     */
    public ResourceCreationException(String message) {
      super(message);
    }

    /**
     * @param message
     *            The message detailing why the resource cannot be created
     * @param cause
     *            The throwable that is the cause of the inability to create
     *            the resource
     */
    public ResourceCreationException(String message, Throwable cause) {
      super(message, cause);
    }
  }

  /**
   * Creates and destroys resources on demand for the pool
   * 
   * @param <T>
   *            The type of value to create and destroy
   */
  public interface ResourceCreator<T> {
    /**
     * Creates a new resource to be available to the pool
     * 
     * @return The new resource
     * @throws ResourceCreationException
     *             If the new resource cannot be created for any reason
     */
    T createResource() throws ResourceCreationException;

    /**
     * Takes care of releasing a resource's system resources when it is no
     * longer needed by the pool. This will only be needed if the pool's
     * maximum size is reduced below the number of resources being managed
     * by the pool.
     * 
     * @param resource
     *            The resource that is no longer being used by the pool
     */
    void destroyResource(T resource);
  }

  private final java.util.concurrent.locks.ReentrantLock theLock;

  private final java.util.ArrayList<T> theAvailableResources;

  private final java.util.ArrayList<T> theInUseResources;

  private final java.util.HashSet<T> theRemovedResources;

  private final java.util.LinkedList<Thread> theWaitingThreads;

  private final ResourceCreator<T> theCreator;

  private int theMaxSize;

  private volatile boolean isClosed;

  /**
   * Creates an empty pool that must be supplied with data via the
   * {@link #addResource(Object)} method
   */
  public ResourcePool() {
    this(null, 0);
  }

  /**
   * Creates an empty pool that is capable of creating its own resources on
   * demand
   * 
   * @param creator
   *            The creator capable of creating resources and capable of
   *            destroying them
   * @param maxSize
   *            The maximum size for the pool
   */
  public ResourcePool(ResourceCreator<T> creator, int maxSize) {
    theLock = new java.util.concurrent.locks.ReentrantLock();
    theAvailableResources = new java.util.ArrayList<T>();
    theInUseResources = new java.util.ArrayList<T>();
    theRemovedResources = new java.util.HashSet<T>();
    theWaitingThreads = new java.util.LinkedList<Thread>();
    theCreator = creator;
    theMaxSize = maxSize;
  }

  /**
   * @return The maximum size of this pool
   * @see #setMaxSize(int)
   */
  public int getMaxSize() {
    return theMaxSize;
  }

  /**
   * Sets the maximum number of resources for this pool. This affects when new
   * resources will be created internally by the resource pool using the
   * creator passed to the {@link #ResourcePool(ResourceCreator, int)}
   * constructor. The parameter does not limit the number of resources that
   * may be given to the pool using the {@link #addResource(Object)} method.
   * 
   * If the maximum size of a pool is reduced, some resources may be destroyed
   * (via {@link ResourceCreator#destroyResource(Object)}).
   * 
   * If this resource pool was created without a creator, this attribute has
   * no effect.
   * 
   * @param size
   *            The maximum size of the pool
   */
  public void setMaxSize(int size) {
    theMaxSize = size;
  }

  /** @return Whether this resource pool has been marked as closed */
  public boolean isClosed() {
    return isClosed;
  }

  /**
   * Clears this resource pool. All available resources will be removed (and
   * destroyed if there is a creator). In-use resources will be removed and
   * destroyed when they are released.
   */
  public void close() {
    isClosed = true;
    theLock.lock();
    try {
      java.util.Iterator<T> iter;
      iter = theAvailableResources.iterator();
      while (iter.hasNext()) {
        T res = iter.next();
        if (theCreator != null)
          theCreator.destroyResource(res);
        iter.remove();
      }
    } finally {
      theLock.unlock();
    }
  }

  /** @return The total number of resources managed by this pool */
  public int getResourceCount() {
    return theAvailableResources.size() + theInUseResources.size();
  }

  /** @return The number of resources in this pool available for use */
  public int getAvailableResourceCount() {
    return theAvailableResources.size();
  }

  /** @return The number of resources in this pool currently being used */
  public int getInUseResourceCount() {
    return theInUseResources.size();
  }

  /**
   * Gets a resource from the pool. The resource will be marked as in-use and
   * will never be given to another invocation of this method nor destroyed by
   * the creator until the resource is released. For this reason, it is
   * advisable to use a try/finally structure to ensure that the resource is
   * released when its use is finished.
   * 
   * In the case that a resource is not currently available (i.e. if all
   * available resources are in use and either there is no creator or the
   * maximum size has been reached), if the wait parameter is true, this
   * method will block until a resource is available. If the wait parameter is
   * false, null will be returned.
   * 
   * @param wait
   *            Whether to wait for the resource rather than accept null if no
   *            resources or available
   * @return The free resource to use, or null if the wait parameter is false
   *         and there are no free resources
   * @throws ResourceCreationException
   *             If an error occurs when the creator creates a new resource
   */
  public T getResource(final boolean wait) throws ResourceCreationException {
    if (isClosed)
      throw new IllegalStateException("This resource pool is closed");
    T ret = null;
    boolean waiting = false;
    do {
      try {
        theLock.lock();
        try {
          if (theAvailableResources.size() == 0)
            updateResourceSet();
          if (theAvailableResources.size() > 0) {
            if (waiting)
              theWaitingThreads.remove(Thread.currentThread());
            waiting = false;
            ret = theAvailableResources
                .remove(theAvailableResources.size() - 1);
            theInUseResources.add(ret);
          } else if (wait && !waiting) {
            waiting = true;
            theWaitingThreads.add(Thread.currentThread());
          }
        } finally {
          theLock.unlock();
        }
        if (waiting) {
          try {
            Thread.sleep(24L * 60 * 60 * 1000);
          } catch (InterruptedException e) {
          }
        }
      } catch (Exception e) {
        if (e instanceof InterruptedException) {
        } else if (e instanceof RuntimeException)
          throw (RuntimeException) e;
        else
          throw new IllegalStateException(
              "Should not be able to get here", e);
      }
    } while (waiting);
    return ret;
  }

  /**
   * Releases a used resource back into the pool for use
   * 
   * @param resource
   *            The resource to release to the pool
   */
  public void releaseResource(T resource) {
    theLock.lock();
    try {
      theInUseResources.remove(resource);
      if (!theRemovedResources.remove(resource)) {
        if (isClosed) {
          if (theCreator != null)
            theCreator.destroyResource(resource);
        } else if (theCreator != null
            && getResourceCount() >= theMaxSize)
          theCreator.destroyResource(resource);
        else
          addResource(resource);
      }
    } finally {
      theLock.unlock();
    }
  }

  /**
   * Adds a resource to this set. After the resource is added, it is treated
   * identically to any other resource in this pool. In particular, if this
   * resource pool is managed by a creator, the new resource may be destroyed
   * by the pool if the pool's maximum size is reduced to make the pool too
   * large.
   * 
   * @param resource
   *            The resource to make available to the pool
   */
  public void addResource(T resource) {
    if (isClosed)
      throw new IllegalStateException("This resource pool is closed");
    Thread waiting = null;
    theLock.lock();
    try {
      if (!theWaitingThreads.isEmpty() || !isClosed) {
        theAvailableResources.add(resource);
        if (!theWaitingThreads.isEmpty())
          waiting = theWaitingThreads.removeFirst();
      }
    } finally {
      theLock.unlock();
    }
    if (waiting != null)
      waiting.interrupt();
  }

  /**
   * Removes a resource from this pool. If the resource no longer exists in
   * the pool, false is returned. If the resource is currently available, it
   * will be removed from the pool completely and false will be returned.
   * Otherwise, the resource is marked for removal and will not be returned to
   * the available resource set (nor will it be destroyed by the creator) when
   * it is released and true will be returned.
   * 
   * @param resource
   *            The resource to remove from the pool
   * @return Whether the resource is still being used
   */
  public boolean removeResource(T resource) {
    theLock.lock();
    try {
      if (theAvailableResources.remove(resource))
        return false;
      if (!theInUseResources.contains(resource))
        return false;
      if (!theRemovedResources.contains(resource))
        theRemovedResources.add(resource);
      return true;
    } finally {
      theLock.unlock();
    }
  }

  private void updateResourceSet() throws ResourceCreationException {
    if (theCreator == null)
      return;
    theLock.lock();
    try {
      int newRC = getNewResourceCount();
      int total = getResourceCount();
      if (newRC < total) { // Need to kill some threads
        int killCount = total - newRC;
        for (; theAvailableResources.size() > 0 && killCount > 0; killCount--) {
          T res = theAvailableResources.remove(theAvailableResources
              .size() - 1);
          if (theCreator != null)
            theCreator.destroyResource(res);
        }
      } else if (newRC > total) {
        int spawnCount = newRC - total;
        for (int t = 0; t < spawnCount; t++)
          theAvailableResources.add(0, theCreator.createResource());
      }
    } finally {
      theLock.unlock();
    }
  }

  private int getNewResourceCount() {
    int used = getInUseResourceCount();
    int total = getResourceCount();
    int ret;
    if (used == total) {
      for (ret = 1; ret * ret <= total; ret++)
        ;
      ret = ret * ret;
    } else {
      int ceilUsedSqrt;
      for (ceilUsedSqrt = 1; ceilUsedSqrt * ceilUsedSqrt < used; ceilUsedSqrt++)
        ;
      int floorTotalSqrt;
      for (floorTotalSqrt = 1; floorTotalSqrt * floorTotalSqrt <= total; floorTotalSqrt++)
        ;
      floorTotalSqrt--;
      if (ceilUsedSqrt < floorTotalSqrt - 1)
        ret = (ceilUsedSqrt + 1) * (ceilUsedSqrt + 1);
      else
        ret = total;
    }
    if (ret > theMaxSize)
      ret = theMaxSize;
    return ret;
  }
}

   
    
    
    
    
    
  








Related examples in the same category

1.Timer Skipping BeepTimer Skipping Beep
2.Timer Schedule a task that executes once every secondTimer Schedule a task that executes once every second
3.Use java.util.Timer to schedule a task to execute once 5 seconds have passedUse java.util.Timer to schedule a task to execute once 5 seconds have passed
4.Timer utilities
5.Timer and TimerTask Classes
6.Pause and start a timer task
7.Create a Timer object
8.Swing also provide a Timer class. A Timer object will send an ActionEvent to the registered ActionListener.
9.Schedule a task by using Timer and TimerTask.
10.Scheduling a Timer Task to Run Repeatedly
11.Create a scheduled task using timer
12.extends TimerTask to create your own task
13.A simple implementation of the Java 1.3 java.util.Timer API
14.Scheduling a Timer Task to Run at a Certain Time
15.Class encapsulating timer functionality
16.Timeout Observer
17.A class that allows a programmer to determine the amount of time spend doing certain routines
18.All times in this Timer object are in milliseconds. The timer starts from the moment of it's creation.
19.Thread Timer
20.Cancel Aware Timer
21.HRTimer is a simple system-wide timer facility using a singleton Timer, with additional instrumentation.
22.Used for timing events with millisecond precision.
23.A class to wait until a condition becomes true.