Adding a Return Thread

We use a separate class, ObjectCreator , to implement the Runnable interface. All Runnable does is wait for requests to come in and then call ThreadedPoo11 s createAndAddObject method: public class ObjectCreator implements Runnable { private ThreadedPool1 _owner; private boolean _requestPending; public ObjectCreatorThreadedPool1 owner { _owner = owner; } public void run { boolean needToCreate = false; while true { synchronizedthis { while _requestPending { try { wait ; } catch InterruptedException ignored{} } needToCreate = _ requestPending; requestPending = false; } if needToCreate { needToCreate = false _owner.createAndAddObject ; } } } public synchronized void askForObject { _requestPending = true; notify ; } }

12.3.6 Adding a Return Thread

Adding a creation thread partially solved two of the four problems we noticed with SimplePool . There is now an upper bound on the number of objects created _maximumSize is used to limit the total number of objects created, as well as the total number of objects stored. And, while creation requests are queued and waiting, objects can still be returned to the pool and will immediately be available to getObject requests. This did not, however, completely solve our second problem. Returning an object involves synchronizing, and thus may block for an extended period of time. To solve this problem, well add another thread, which will return objects to the pool. Moreover, in doing so, we will solve our third problem: The third major problem with SimplePool is that the validity checks, and the destruction of surplus pooled objects, are done in the calling thread. One of the big benefits of using a pool is that it makes the client threads more responsive by offloading most of their work to the pool. Wed certainly like to move validation and destruction out of the client threads as well. Adding this thread involves a few small changes to the actual pool object. In particular, in the class ThreadedPool2 , we rewrote returnObject to simply delegate all of its functionality to the background thread. We also added a new method, startReturnerThread , which is called by ThreadedPool2 s constructor. ThreadedPool2 is otherwise almost identical to ThreadedPool1 : public void returnObjectObject object { _returner.validateAndReturnobject; } protected void returnObjectToPoolObject object { called from background thread if _helper.isObjectStillValidobject { _availableObjects.addobject; notifyWaitingGets ; } else { _helper.disposeobject; _totalNumberOfObjects--; } return; } private void startReturnerThreadString poolName { _returner = new ObjectReturnerthis; Thread returnerThread = new Thread_returner, poolName + returner thread; returnerThread.setPriorityThread.NORM_PRIORITY+2; returnerThread.start ; } Theres a general programming pattern here. Grady Booch is generally credited with the aphorism, If the design of your program is too complicated, add more objects. Similarly, if you are having threading or synchronization problems, the solution is usually to add more threads. Both statements are ludicrous and counterintuitive. But theyre also both true, and moreover, theyre both really specializations of the old saw that adding another level of indirection never hurt anything. The returner thread has a fairly high priority because were biased towards returning objects to the pool and reusing them. We dont want client threads to perform the return and validation operations, but we definitely want the return thread to return objects to the pool as quickly as possible. For example, if validating an object requires calling a database, wed rather not have the client thread wait for that operation to complete. _returner is an instance of the ObjectReturner class. What happens inside ObjectReturner is simple. When validateAndReturnObject is called, the instance of ObjectReturner places the argument inside a vector and notifies the background thread. The background thread handles all the validation and reclamation work by calling the pools protected returnObjectToPool method. Meanwhile, the original calling thread, presumably a client thread, returns from validateAndReturnObject and can continue processing without anymore delays: public class ObjectReturner implements Runna ble { private Vector _objectsToReturn; private ThreadedPool2 _owner; public ObjectReturnerThreadedPool2 owner { _owner = owner; _objectsToReturn = new Vector ; } public void run { while true { Object objectToReturn; while 0==_objectsToReturn.size { synchronized _objectsToReturn { try { _objectsToReturn.wait ; } catch InterruptedException e{} } } int lastIndex = _objectsToReturn.size -1; objectToReturn = _objectsToReturn.removelastIndex; _owner.returnObjectToPoolobjectToReturn; } } public void validateAndReturnObject object { synchronized_objectsToReturn { _objectsToReturn.addobject; if 1== _objectsToReturn.size { _objectsToReturn.notify ; } } } }

12.3.7 Gradually Shrinking the Pool