Build your own ObjectPool in Java, Part 2

Go beyond increased app speed and reduced memory requirements: Optimize and stabilize your ObjectPool utility

Object pooling is a simple and elegant way to conserve memory and enhance speed in Java applications. By sharing and reusing objects, processes (threads) aren't bogged down by the instantiation and loading time of new objects, or the overhead of excessive garbage collection.

In Build your own ObjectPool in Java, Part 1, I showed you how to implement a pool of database connections. In this article, we'll expand on that example, adding exception propagation and a clean-up thread.

Exception propagation

To make the object pool as transparent as possible, the borrow and/or get methods should throw the same exception(s) as the constructor of the desired object. Propagation of these exceptions is a simple matter of casting.

Starting from the bottom, the source of any exceptions will be found in the abstraction of the ObjectPool's create() method. Therefore, we simply add the throws Exception qualifier to its declaration.


abstract Object create() throws Exception;

In the JDBCConnectionPool abstraction of the create() method, a single call to DriverManger.getConnection() is made. This can throw an SQLException. Rather than catching the exception and throwing it away, as we did in the first article, we'll propagate it.


Object create() throws SQLException
{  
   return( DriverManager.getConnection( dsn, usr, pwd ) );
}

ObjectPool calls the create() method from checkOut(). Since create() now throws an exception, the checkOut() method must deal with it or pass it on. We'll do the latter by simply adding throws Exception to the declaration of checkOut().


synchronized Object checkOut() throws Exception

Now that the exception has reached the borrowConnection() method of JDBCConnectionPool, it's cast back to an SQLException and passed on to the process that requested the database connection.


public Connection borrowConnection() throws SQLException
{ 
   try
   {
      return( ( Connection ) super.checkOut() );
   }
   catch( Exception e )
   {
      throw( (SQLException) e );
   } 
}

The following blocks of code are now practically interchangeable, because they return the same object and throw the same exception:


Class.forName( ... ).newInstance();
Connection c = DriverManager.getConnection( ... );
JDBCConnectionPool cp = new JDBCConnectionPool( ... );
Connection c = cp.borrowConnection( ... );

The clean-up thread

As stated in the previous article, there is a condition in which the pool can accumulate a lot of objects that never get cleaned up. On top of that, it's rather inefficient to have the expiration logic in the checkOut() method. So we'll kill two birds with one stone (or thread, actually). We start by moving the expiration logic into a separate method called cleanUp(). Then we write a simple little thread that calls this method periodically.

Our thread needs only a pointer back to the object pool and a specified sleep time (passed in via the constructor).


class CleanUpThread extends Thread
{
   private ObjectPool pool;
   private long sleepTime;
   CleanUpThread( ObjectPool pool, long sleepTime )
   {
      this.pool = pool;
      this.sleepTime = sleepTime;
   }
   public void run()
   {
      while( true )
      {
         try
         {
            sleep( sleepTime );
         }
         catch( InterruptedException e )
         {
            // ignore it
         }         
         pool.cleanUp();
      }
   }
}

The thread is instantiated and started in ObjectPool's constructor.

cleaner = new CleanUpThread( this, expirationTime );
cleaner.start();

The new cleanUp() method, which gets called by the thread every expirationTime millisecond, simply releases all the old objects in the pool and asks the garbage collector to reclaim them.


synchronized void cleanUp()
{
   Object o;
   long now = System.currentTimeMillis();        
   Enumeration e = unlocked.keys();  
   while( e.hasMoreElements() )
   {
      o = e.nextElement();        
      if( ( now - ( ( Long ) unlocked.get( o ) ).longValue() ) >
expirationTime )
      {
         unlocked.remove( o );
         expire( o );
         o = null;
      }
   }
   System.gc();
}

Check out the full source for ObjectPool to see what checkOut() looks like without the expiration logic in it. The full source for JDBCConnectionPool is available as well.

Conclusion

With exception propagation and the clean-up thread, the ObjectPool class becomes more robust and one step closer to being transparent. I hope this tool proves as useful for you as it has been to me.

Thomas E. Davis is a Sun Certified Java Programmer. Although he was weaned on Linux boxes and Perl code (and misses them dearly), he currently pays his rent by developing Java back-end services on Windows NT.

This story, "Build your own ObjectPool in Java, Part 2" was originally published by JavaWorld.

Copyright © 1998 IDG Communications, Inc.