通用并发对象池

在本文中,我们将介绍如何在Java中创建对象池。 近年来,JVM的性能成倍增加,大多数类型的对象几乎都变得多余,从而提高了对象池的性能。 从本质上讲,对象的创建不再像以前那样昂贵。

但是,有些对象在创建时肯定会付出高昂的代价。 诸如线程,数据库连接对象等对象不是轻量级对象,并且创建起来会稍微贵一些。 在任何应用程序中,我们都需要使用上述多种对象。 因此,如果有一种非常容易的方法可以轻松创建和维护该类型的对象池,那么可以动态使用和重用对象,而不必担心客户端代码对对象的生存周期的影响,那就太好了。
在实际编写对象池的代码之前,让我们首先确定任何对象池必须回答的主要要求。

  • 池必须允许客户端使用对象(如果有)。
  • 一旦客户端将对象返回到池中,它必须重新使用对象。
  • 如果需要,它必须能够创建更多对象以满足客户不断增长的需求。
  • 它必须提供适当的关闭机制,以便在关闭时不会发生内存泄漏。

毋庸置疑,以上几点将构成我们向客户公开的界面的基础。
因此,我们的接口声明如下:

package com.test.pool;


/**
 * Represents a cached pool of objects.
 * 
 * @author Swaranga
 *
 * @param < T > the type of object to pool.
 */
public interface Pool< T >
{
 /**
  * Returns an instance from the pool. 
  * The call may be a blocking one or a non-blocking one 
  * and that is determined by the internal implementation.
  * 
  * If the call is a blocking call, 
  * the call returns immediately with a valid object 
  * if available, else the thread is made to wait 
  * until an object becomes available.
  * In case of a blocking call, 
  * it is advised that clients react 
  * to {@link InterruptedException} which might be thrown
  * when the thread waits for an object to become available.
  * 
  * If the call is a non-blocking one, 
  * the call returns immediately irrespective of 
  * whether an object is available or not.
  * If any object is available the call returns it 
  * else the call returns < code >null< /code >.
  * 
  * The validity of the objects are determined using the
  * {@link Validator} interface, such that 
  * an object < code >o< /code > is valid if 
  * < code > Validator.isValid(o) == true < /code >.
  * 
  * @return T one of the pooled objects.
  */
 T get();
 
 /**
  * Releases the object and puts it back to the pool.
  * 
  * The mechanism of putting the object back to the pool is
  * generally asynchronous, 
  * however future implementations might differ.
  * 
  * @param t the object to return to the pool
  */
  
 void release(T t);
 
 /**
  * Shuts down the pool. In essence this call will not 
  * accept any more requests 
  * and will release all resources.
  * Releasing resources are done 
  * via the < code >invalidate()< /code >
  * method of the {@link Validator} interface.
  */
 
 void shutdown();
}

特意使上述接口非常简单通用,以支持任何类型的对象。 它提供了从池中获取对象或将对象返回池中的方法。 它还提供了一种关闭机制来处理对象。

现在,我们尝试创建上述接口的实现。 但在此之前,必须注意,理想的release()方法将首先尝试检查客户端返回的对象是否仍然可重用,这一点很重要。 如果是,则它将返回到池中,否则必须丢弃该对象。 我们希望Pool接口的每个实现都遵循此规则。 因此,在创建具体的实现之前,我们创建一个抽象的实现帽子,将这种限制强加给后续的实现。 我们的抽象实现将被称为Surprise,AbstractPool,其定义如下:

package com.test.pool;

/**
 * Represents an abstract pool, that defines the procedure
 * of returning an object to the pool.
 * 
 * @author Swaranga
 *
 * @param < T > the type of pooled objects.
 */
abstract class AbstractPool < T > implements Pool < T >
{
 /**
  * Returns the object to the pool. 
  * The method first validates the object if it is
  * re-usable and then puts returns it to the pool.
  * 
  * If the object validation fails, 
  * some implementations
  * will try to create a new one 
  * and put it into the pool; however 
  * this behaviour is subject to change 
  * from implementation to implementation
  * 
  */
 @Override
 public final void release(T t)
 {
  if(isValid(t))
  {
   returnToPool(t);
  }
  else
  {
   handleInvalidReturn(t);
  }
 }
 
 protected abstract void handleInvalidReturn(T t);
 
 protected abstract void returnToPool(T t);
 
 protected abstract boolean isValid(T t);
}

在上面的类中,我们强制对象池必须先验证对象,然后再将其返回到池中。 为了自定义其池的行为,实现可以自由选择它们实现三种抽象方法的方式。 他们将决定使用自己的逻辑,如何检查对象是否对重用有效[validate()方法,如果客户端返回的对象无效[该方法,handleInvalidReturn()方法]以及实际逻辑,该怎么办。将有效对象返回到池中[returnToPool()方法]。

现在有了上面的类集,我们几乎可以进行具体的实现了。 但是要注意的是,由于上述类旨在支持通用对象池,因此上述类的通用实现将不知道如何验证对象[因为对象将是通用的:-)。 因此,我们需要其他可以帮助我们的东西。

我们实际上需要的是一种验证对象的常用方法,这样,具体的Pool实现就不必担心正在验证的对象的类型。 因此,我们引入了一个新的接口Validator,该接口定义了验证对象的方法。 我们对Validator接口的定义如下:

package com.test.pool;

 /**
  * Represents the functionality to 
  * validate an object of the pool
  * and to subsequently perform cleanup activities.
  * 
  * @author Swaranga
  *
  * @param < T > the type of objects to validate and cleanup.
  */
 public static interface Validator < T >
 {
  /**
   * Checks whether the object is valid.
   * 
   * @param t the object to check.
   * 
   * @return true 
   * if the object is valid else false .
   */
  public boolean isValid(T t);
  
  /**
   * Performs any cleanup activities 
   * before discarding the object.
   * For example before discarding 
   * database connection objects,
   * the pool will want to close the connections. 
   * This is done via the 
   * invalidate() method.
   * 
   * @param t the object to cleanup
   */
  
  public void invalidate(T t);
 }

上面的接口定义了检查对象是否有效的方法,以及使对象和对象无效的方法。 当我们要丢弃对象并清除该实例使用的任何内存时,应使用invalidate方法。 请注意,此接口本身没有什么意义,仅在对象池的上下文中使用时才有意义。 因此,我们在顶级Pool接口中定义此接口。 这类似于Java Collections库中的Map和Map.Entry接口。 因此,我们的Pool接口如下所示:

package com.test.pool;


/**
 * Represents a cached pool of objects.
 * 
 * @author Swaranga
 *
 * @param < T > the type of object to pool.
 */
public interface Pool< T >
{
 /**
  * Returns an instance from the pool. 
  * The call may be a blocking one or a non-blocking one 
  * and that is determined by the internal implementation.
  * 
  * If the call is a blocking call, 
  * the call returns immediately with a valid object 
  * if available, else the thread is made to wait 
  * until an object becomes available.
  * In case of a blocking call, 
  * it is advised that clients react 
  * to {@link InterruptedException} which might be thrown
  * when the thread waits for an object to become available.
  * 
  * If the call is a non-blocking one, 
  * the call returns immediately irrespective of 
  * whether an object is available or not.
  * If any object is available the call returns it 
  * else the call returns < code >null< /code >.
  * 
  * The validity of the objects are determined using the
  * {@link Validator} interface, such that 
  * an object < code >o< /code > is valid if 
  * < code > Validator.isValid(o) == true < /code >.
  * 
  * @return T one of the pooled objects.
  */
 T get();
 
 /**
  * Releases the object and puts it back to the pool.
  * 
  * The mechanism of putting the object back to the pool is
  * generally asynchronous, 
  * however future implementations might differ.
  * 
  * @param t the object to return to the pool
  */
  
 void release(T t);
 
 /**
  * Shuts down the pool. In essence this call will not 
  * accept any more requests 
  * and will release all resources.
  * Releasing resources are done 
  * via the < code >invalidate()< /code >
  * method of the {@link Validator} interface.
  */
 
 void shutdown();

 /**
  * Represents the functionality to 
  * validate an object of the pool
  * and to subsequently perform cleanup activities.
  * 
  * @author Swaranga
  *
  * @param < T > the type of objects to validate and cleanup.
  */
 public static interface Validator < T >
 {
  /**
   * Checks whether the object is valid.
   * 
   * @param t the object to check.
   * 
   * @return true 
   * if the object is valid else false .
   */
  public boolean isValid(T t);
  
  /**
   * Performs any cleanup activities 
   * before discarding the object.
   * For example before discarding 
   * database connection objects,
   * the pool will want to close the connections. 
   * This is done via the 
   * invalidate() method.
   * 
   * @param t the object to cleanup
   */
  
  public void invalidate(T t);
 }
}

我们几乎准备好具体实施了。 但是在此之前,我们需要一种最终的武器,它实际上是对象池中最重要的武器。 这被称为“创建新对象的能力”。c我们的对象池将是通用的,它们必须具有如何创建新对象以填充其池的知识。 此功能也必须不依赖于对象池的类型,并且必须是创建新对象的常用方法。 完成此操作的方法将是一个称为ObjectFactory的接口,该接口仅定义一种方法,即“如何创建新对象”。 我们的ObjectFactory接口如下:

package com.test.pool;

/**
 * Represents the mechanism to create 
 * new objects to be used in an object pool.
 * 
 * @author Swaranga
 *
 * @param < T > the type of object to create. 
 */
public interface ObjectFactory < T >
{
 /**
  * Returns a new instance of an object of type T.
  * 
  * @return T an new instance of the object of type T
  */
 public abstract T createNew();
}

最后,我们完成了我们的帮助程序类,现在我们将创建Pool接口的具体实现。 因为我们想要一个可以在并发应用程序中使用的池,所以我们将创建一个阻塞池,如果池中没有可用的对象,它将阻塞客户端。 阻塞机制将无限期阻塞,直到对象可用为止。 这种实现方式催生了另一个方法,该方法将仅在给定的超时时间段内阻塞,如果在返回该对象的超时之前有任何对象可用,否则在超时之后而不是永远等待,则返回一个空对象。 此实现类似于Java Concurrency API的LinkedBlockingQueue实现,因此在实现实际的类之前,我们公开另一个实现BlockingPool,该实现类似于Java Concurrency API的BlockingQueue接口。

因此,Blockingpool接口声明如下:

package com.test.pool;

import java.util.concurrent.TimeUnit;

/**
 * Represents a pool of objects that makes the 
 * requesting threads wait if no object is available.
 * 
 * @author Swaranga
 *
 * @param < T > the type of objects to pool.
 */
public interface BlockingPool < T > extends Pool < T >
{
 /**
  * Returns an instance of type T from the pool.
  * 
  * The call is a blocking call, 
  * and client threads are made to wait
  * indefinitely until an object is available. 
  * The call implements a fairness algorithm 
  * that ensures that a FCFS service is implemented.
  * 
  * Clients are advised to react to InterruptedException. 
  * If the thread is interrupted while waiting 
  * for an object to become available,
  * the current implementations 
  * sets the interrupted state of the thread 
  * to true and returns null. 
  * However this is subject to change 
  * from implementation to implementation.
  * 
  * @return T an instance of the Object 
  * of type T from the pool.
  */
 T get();
 
 /**
  * Returns an instance of type T from the pool, 
  * waiting up to the
  * specified wait time if necessary 
  * for an object to become available..
  * 
  * The call is a blocking call, 
  * and client threads are made to wait
  * for time until an object is available 
  * or until the timeout occurs. 
  * The call implements a fairness algorithm 
  * that ensures that a FCFS service is implemented.
  * 
  * Clients are advised to react to InterruptedException. 
  * If the thread is interrupted while waiting 
  * for an object to become available,
  * the current implementations 
  * set the interrupted state of the thread 
  * to true and returns null. 
  * However this is subject to change 
  * from implementation to implementation.
  *  
  * 
  * @param time amount of time to wait before giving up, 
  *   in units of unit
  * @param unit a TimeUnit determining 
  *   how to interpret the
  *        timeout parameter
  *        
  * @return T an instance of the Object 
  * of type T from the pool.
  *        
  * @throws InterruptedException 
  * if interrupted while waiting
  */
 
 T get(long time, TimeUnit unit) throws InterruptedException;
}

我们的BoundedBlockingPool实现将如下所示:

package com.test.pool;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public final class BoundedBlockingPool < T > 
 extends AbstractPool < T >
 implements BlockingPool < T >
{
 private int size;
 
 private BlockingQueue < T > objects;
 
 private Validator < T > validator;
 private ObjectFactory < T > objectFactory;
 
 private ExecutorService executor = 
  Executors.newCachedThreadPool();
  
 private volatile boolean shutdownCalled;
 
 public BoundedBlockingPool(
   int size, 
   Validator < T > validator, 
   ObjectFactory < T > objectFactory)
 {
  super();
  
  this.objectFactory = objectFactory;
  this.size = size;
  this.validator = validator;
  
  objects = new LinkedBlockingQueue < T >(size);
  
  initializeObjects();
  
  shutdownCalled = false;
 }
 
 public T get(long timeOut, TimeUnit unit)
 {
  if(!shutdownCalled)
  {
   T t = null;
   
   try
   {
    t = objects.poll(timeOut, unit);
    
    return t;
   }
   catch(InterruptedException ie)
   {
    Thread.currentThread().interrupt();
   }
   
   return t;
  }
  
  throw new IllegalStateException(
   'Object pool is already shutdown');
 }
 
 public T get()
 {
  if(!shutdownCalled)
  {
   T t = null;
   
   try
   {
    t = objects.take();
   }
   catch(InterruptedException ie)
   {
    Thread.currentThread().interrupt();
   }
   
   return t;
  }
  
  throw new IllegalStateException(
   'Object pool is already shutdown');
 }
 
 public void shutdown()
 {
  shutdownCalled = true;
  
  executor.shutdownNow();
  
  clearResources();
 }
 
 private void clearResources()
 {
  for(T t : objects)
  {
   validator.invalidate(t);
  }
 }
 
 @Override
 protected void returnToPool(T t)
 {
  if(validator.isValid(t))
  {
   executor.submit(new ObjectReturner(objects, t));
  }
 }
 
 @Override
 protected void handleInvalidReturn(T t)
 {
  
 }
 
 @Override
 protected boolean isValid(T t)
 {
  return validator.isValid(t);
 }
 
 private void initializeObjects()
 {
  for(int i = 0; i < size; i++)
  {
   objects.add(objectFactory.createNew());
  }
 }
 
 private class ObjectReturner < E > 
            implements Callable < Void >
 {
  private BlockingQueue < E > queue;
  private E e;
  
  public ObjectReturner(BlockingQueue < E > queue, E e)
  {
   this.queue = queue;
   this.e = e;
  }
  
  public Void call()
  {
   while(true)
   {
    try
    {
     queue.put(e);
     break;
    }
    catch(InterruptedException ie)
    {
     Thread.currentThread().interrupt();
    }
   }
   
   return null;
  }
 }
}

上面是一个非常基本的对象池,在内部由LinkedBlockingQueue支持。 唯一感兴趣的方法是returnToPool()方法。 由于内部存储是一个阻塞池,因此,如果我们尝试将返回的元素直接放入LinkedBlockingPool中,则如果队列已满,它可能会阻塞客户端。 但是我们不希望对象池的客户端仅为了执行普通任务(例如将对象返回到池)而阻塞。 因此,我们完成了将对象作为异步任务插入LinkedBlockingQueue的实际任务,并将其提交给Executor实例,以便客户端线程可以立即返回。

现在,我们将上述对象池用于代码中。 我们将使用对象池来池一些数据库连接对象。 因此,我们将需要一个验证器来验证我们的数据库连接对象。

我们的JDBCConnectionValidator将如下所示:

package com.test;

import java.sql.Connection;
import java.sql.SQLException;

import com.test.pool.Pool.Validator;

public final class JDBCConnectionValidator 
    implements Validator < Connection >
{
 public boolean isValid(Connection con)
 { 
  if(con == null)
  {
   return false;
  }
  
  try
  {
   return !con.isClosed();
  }
  catch(SQLException se)
  {
   return false;
  }
 }
 
 public void invalidate(Connection con)
 {
  try
  {
   con.close();
  }
  catch(SQLException se)
  {
   
  }
 }
}

我们的JDBCObjectFactory将使对象池能够创建新对象,如下所示:

package com.test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import com.test.pool.ObjectFactory;

public class JDBCConnectionFactory 
 implements ObjectFactory < Connection >
{
 private String connectionURL;
 private String userName;
 private String password;
  
 public JDBCConnectionFactory(
  String driver, 
  String connectionURL, 
  String userName, 
  String password)
        {
         super();
         
         try
         {
          Class.forName(driver);
         }
         catch(ClassNotFoundException ce)
         {
          throw new IllegalArgumentException(
           'Unable to find driver in classpath', ce);
         }
         
         this.connectionURL = connectionURL;
         this.userName = userName;
         this.password = password;
        }
 
 public Connection createNew()
 { 
  try
  {
   return 
       DriverManager.getConnection(
    connectionURL, 
    userName, 
    password);
  }
  catch(SQLException se)
  {
   throw new IllegalArgumentException(
    'Unable to create new connection', se);
  }
 }
}

现在,我们使用上面的Validator和ObjectFactory创建一个JDBC对象池:

package com.test;
import java.sql.Connection;

import com.test.pool.Pool;
import com.test.pool.PoolFactory;


public class Main
{
 public static void main(String[] args)
    {
  Pool < Connection > pool = 
   new BoundedBlockingPool < Connection > (
    10, 
    new JDBCConnectionValidator(),
    new JDBCConnectionFactory('', '', '', '')
    );
  
  //do whatever you like
    }
}

作为阅读整个帖子的奖励。 我将提供Pool接口的另一种实现,它实际上是一个非阻塞对象池。 此实现与上一个实现的唯一区别在于,如果某个元素不可用,则此实现不会阻塞客户端,而是返回null。 它去了:

package com.test.pool;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Semaphore;

public class BoundedPool < T > 
 extends AbstractPool < T >
{
 private int size;
 
 private Queue < T > objects;
 
 private Validator < T > validator;
 private ObjectFactory < T > objectFactory;
 
 private Semaphore permits;
  
 private volatile boolean shutdownCalled;
 
 public BoundedPool(
  int size, 
  Validator < T > validator, 
  ObjectFactory < T > objectFactory)
 {
  super();
  
  this.objectFactory = objectFactory;
  this.size = size;
  this.validator = validator;
  
  objects = new LinkedList < T >();
  
  initializeObjects();
  
  shutdownCalled = false;
 }
 
 
 @Override
 public T get()
 {
  T t = null;
  
  if(!shutdownCalled)
  {
   if(permits.tryAcquire())
   {
    t = objects.poll();
   }
  }
  else
  {
   throw new IllegalStateException(
    'Object pool already shutdown');
  }
  
  return t;
 }

 @Override
 public void shutdown()
 {
  shutdownCalled = true;
  
  clearResources();
 }
 
 private void clearResources()
 {
  for(T t : objects)
  {
   validator.invalidate(t);
  }
 }

 @Override
 protected void returnToPool(T t)
 {
  boolean added = objects.add(t);
  
  if(added)
  {
   permits.release();
  }
 }
 
 @Override
 protected void handleInvalidReturn(T t)
 {
  
 }

 @Override
 protected boolean isValid(T t)
 {
  return validator.isValid(t);
 }
 
 private void initializeObjects()
 {
  for(int i = 0; i < size; i++)
  {
   objects.add(objectFactory.createNew());
  }
 }
}

考虑到我们现在有两个强大的实现,最好让用户通过带有有意义名称的工厂创建我们的池。 这是工厂:

package com.test.pool;

import com.test.pool.Pool.Validator;

/**
 * Factory and utility methods for 
 * {@link Pool} and {@link BlockingPool} classes 
 * defined in this package. 
 * This class supports the following kinds of methods:
 *
 *
  • *
  • 创建并返回{@link Pool}接口的默认非阻塞*实现的方法。 *
  • * *
  • 创建并返回{@link BlockingPool}接口的默认实现的方法。 *
  • *
* * @author Swaranga * /公共最终类PoolFactory {private PoolFactory(){} / ** *创建一个并返回一个新的对象池,它是{@link BlockingPool}的实现,*的大小受* size参数。 * * @param size池中对象的数量。 * @param factory工厂创建新对象。 * @paramvalidator验证器,用于验证返回对象的可重用性。 * * @返回阻塞的对象池*由大小限制* / public static <T> Pool <T> newBoundedBlockingPool(int size,ObjectFactory <T> factory,Validator <T> validator){返回新的BoundedBlockingPool <T>(size,验证人,工厂); } / ** *创建一个并返回一个新的对象池,该对象池是{@link Pool}的一个实现,该对象的大小受size参数限制。 * * @param size池中对象的数量。 * @param factory工厂创建新对象。 * @param验证器验证器,以验证*返回对象的可重用性。 * * @返回一个受大小限制的对象池* / public static <T> Pool <T> newBoundedNonBlockingPool(int size,ObjectFactory <T> factory,Validator <T> validator){返回新的BoundedPool <T>(大小,验证器,厂); }}

因此,我们的客户现在可以以更易读的方式创建对象池:

package com.test;
import java.sql.Connection;

import com.test.pool.Pool;
import com.test.pool.PoolFactory;


public class Main
{
 public static void main(String[] args)
    {
  Pool < Connection > pool = 
   PoolFactory.newBoundedBlockingPool(
    10, 
    new JDBCConnectionFactory('', '', '', ''), 
    new JDBCConnectionValidator());
  
  //do whatever you like
    }
}

这样就结束了我们的长篇文章。 这个早就该了。 随时使用,更改,添加更多实现。

祝您编程愉快,别忘了分享!

参考: The Java HotSpot博客上的JCG合作伙伴 Sarma Swaranga 提供的通用并发对象池


翻译自: https://www.javacodegeeks.com/2012/09/a-generic-and-concurrent-object-pool.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值