java:executors + tasks + locks

时间:2011-02-02 16:06:11

标签: java concurrency locking semaphore

假设我有一个ExecutorService(可以是一个线程池,因此涉及并发),它可以在不同的时间执行任务,定期或响应某些其他条件。要执行的任务如下:

  • 如果此任务已在进行中,则不执行任何操作(并让以前运行的任务完成)。
  • 如果此任务尚未进行,请运行算法X,这可能需要很长时间。

我正在考虑实现这一目标的方法。它应该是这样的:

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

其中SomeObjReentrantLock(获取= tryLock()和发布= unlock())或者是AtomicBoolean等等,但我不确定是哪一个。我在这里需要一个ReentrantLock吗? (也许我想要一个不可重入的锁,以防algorithmX()导致这个任务以递归方式运行!或者AtomicBoolean会不够?


编辑:对于非重入锁定,这是否合适?

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

4 个答案:

答案 0 :(得分:2)

你建议的锁定实现很弱,因为某人不方便地使用它会非常容易。

下面是一个更有效的实现,具有与您的实现相同的不正确的使用弱点:

   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);
   }

答案 1 :(得分:2)

你的第一段代码看起来很不错,但如果你担心 algorithmX 以递归方式调用任务,我建议你使用 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();
          }
       }
   }
}

请特别注意使用尝试 获取。如果获取锁定失败,则 algorithmX 不会运行。

答案 2 :(得分:0)

ReentrantLock对我来说似乎很好。我发现使用AtomicInteger手动创建锁定感兴趣的唯一情况是,如果您的确非常短algorithmX,则不是您的情况。

答案 3 :(得分:0)

我认为选择合适锁定的秘诀是: *如果此任务已在进行中,则不执行任何操作(并让以前运行的任务完成)。

在这种情况下,“无为”意味着什么?运行 algorithmX 后,线程应该阻塞并重试执行?如果是这种情况,则应使用 semaphore.acquire 而不是 tryAcquire ,并且AtomicBoolean解决方案将无法按预期工作。