I'm developing a Java app that for much of the time, including the point of shutdown, is having to deal with a flood of incoming asynchronous calls from a foreign framework. During normal operation these incoming calls then need to be dispatched to another framework, again asynchronously.
At the moment I'm having my module be a "good" citizen and do some locking around a shutdown flag which, once set, will gracefully cease the dispatch of any further outgoing calls.
The troubling thing is that because both incoming and outgoing calls are asynchronous, I'm having to make each "worker" task perform two sets of locking (see below) to do the same shutdown flag check (EDIT: It's been pointed out to me in another question that using Semaphores only one acquire/release is needed for each worker). It works, but there are many of these worker tasks to handle and I worry about the cumulative slowdown in performance. Profiling will come soon once the framework is expanded a little but regardless of the result it'd be good to follow best practices.
An alternative is to simply do no shutdown flag check locking and handle the anticipated exceptions that are generated when the external frameworks are shutdown before the async calls have finished processing. I should add that there are no detrimental operational effects if this approach is taken. Both methods will result in a clean shutdown.
Your ideas on which is the better practice, please? Heavy-handed locking with no exceptions, versus no locking but a barrage of exceptions.
With locks, the worker task code looks something like this:
final private ReentrantReadWriteLock shutdownLock = new ReentrantReadWriteLock();
private boolean isShutdown;
private void workerTask()
{
try
{
shutdownLock.readLock().lock();
if (isShutdown)
return;
executeAsynchronously(new Runnable()
{
@Override
final public void run()
{
try
{
shutdownLock.readLock().lock();
if (isShutdown)
return;
// Do something here.
}
finally
{
shutdownLock.readLock().unlock();
}
}
});
}
finally
{
shutdownLock.readLock().unlock();
}
}
The shutdown method requests the shutdownLock.writeLock(), then sets the isShutdown flag.
An alternative without locking and anticipating the shutdown-generated exceptions looks something like this:
volatile private boolean isShutdown;
private void workerTask()
{
try
{
executeAsynchronously(new Runnable()
{
@Override
final public void run()
{
try
{
// Do something here.
}
catch (final FrameworkRuntimeException exception)
{
if ((! isShutdown) || (exception.type !=
FrameworkRuntimeException.FrameworkIsDisposed))
throw exception;
}
}
});
}
catch (final FrameworkRuntimeException exception)
{
if ((! isShutdown) || (exception.type !=
FrameworkRuntimeException.FrameworkIsDisposed))
throw exception;
}
}
The shutdown method for this implementation sets the volatile isShutdown flag to true.
Thanks in advance for any feedback,
Russ
EDIT: It's been helpfully pointed out to me in another question that I could use a Semaphore to avoid the double locking in the first approach, so it wouldn't be so heavy-handed after all, but the question still stands.