Java: executors + tasks + locks

Suppose I have an ExecutorService (which can be a thread pool, so there is concurrency) that executes the task at different times, periodically or in response to some other condition. The task to be completed is as follows:

  • If this task is already being completed, do nothing (and let the finished work end).
  • If this task is not already running, run Algorithm X, which can take a long time.

I am trying to come up with 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

SomeObj ReentrantLock ( = tryLock() release = unlock()), AtomicBoolean -, , . ReentrantLock ? ( , , algorithmX() !) AtomicBoolean?


edit: , ?

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 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.
        */
   }
}
+5
4

, , , - .

, , :

   AtomicBoolean inProgress = new AtomicBoolean(false)
   /* Returns true if we acquired the lock */
   private boolean acquire() {
       return inProgress.compareAndSet(false, true);
   }
   /** Always release lock without determining if we in fact hold it */
   private void release() {
       inProgress.set(false);
   }
+2

, , X , java.util.concurrent.Semaphore , ReentrantLock. :

Runnable task = new Runnable() {
   final Semaphore lock = new Semaphore( 1 );
   @Override public void run() {
       if (lock.tryAcquire())
       {
          try
          {
             algorithmX();
          }
          finally
          {
             lock.release();
          }
       }
   }
}

, , try. , X .

+2

ReentrantLock . , AtomicInteger, , algorithmX, .

0

I think that the secret to choosing the right lock is as follows: * if this task is already in progress, do nothing (and let the previous task end).

What does “do nothing” mean in this context? Thread should block and retry execution after running algorithmX . If so, then a semaphore. Aacquire should be used instead of tryAcquire, and the AtomicBoolean solution will not work properly.

0
source

All Articles