Suppose I have an ExecutorService (which can be a thread pool, so there’s concurrency involved) which executes a task at various times, either periodically or in response to some other condition. The task to be executed is the following:
- if this task is already in progress, do nothing (and let the previously-running task finish).
- if this task is not already in progress, run Algorithm X, which can take a long time.
I’m trying to think of a way to implement this. It should be something like:
Runnable task = new Runnable() {
final SomeObj inProgress = new SomeObj();
@Override public void run() {
if (inProgress.acquire())
{
try
{
algorithmX();
}
finally
{
inProgress.release();
}
}
}
}
// re-use this task object whenever scheduling the task with the executor
where SomeObj is either a ReentrantLock (acquire = tryLock() and release = unlock()) or an AtomicBoolean or something, but I’m not sure which. Do I need a ReentrantLock here? (Maybe I want a non-reentrant lock in case algorithmX() causes this task to be run recursively!) Or would an AtomicBoolean be enough?
edit: for a non-reentrant lock, is this appropriate?
Runnable task = new Runnable() {
boolean inProgress = false;
final private Object lock = new Object();
/** try to acquire lock: set inProgress to true,
* return whether it was previously false
*/
private boolean acquire() {
synchronized(this.lock)
{
boolean result = !this.inProgress;
this.inProgress = true;
return result;
}
}
/** release lock */
private void release() {
synchronized(this.lock)
{
this.inProgress = false;
}
}
@Override public void run() {
if (acquire())
{
// nobody else is running! let's do algorithmX()
try
{
algorithmX();
}
finally
{
release();
}
}
/* otherwise, we are already in the process of
* running algorithmX(), in this thread or in another,
* so don't do anything, just return control to the caller.
*/
}
}
The lock implementation you suggest is weak in the sense that it would be quite easy for someone to use it improperly.
Below is a much more efficient implementation with the same improper use weaknesses as your implementation: