簡體   English   中英

java:executors + tasks + locks

[英]java: executors + tasks + locks

假設我有一個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 (acquire = tryLock()和release = 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.
        */
   }
}

你建議的鎖定實現很弱,因為某人很容易不正確地使用它。

下面是一個更有效的實現,具有與您的實現相同的不正確的使用弱點:

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

你的第一段代碼看起來很不錯,但如果你擔心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();
          }
       }
   }
}

特別注意try acquire的使用。 如果獲取鎖定失敗,則不運行algorithmX

ReentrantLock對我來說似乎很好。 我發現使用AtomicInteger手動創建鎖定的唯一情況是,如果你有一個非常簡短的algorithmX AtomicInteger而不是你的情況。

我認為選擇正確的鎖定impl的秘訣是:*如果此任務已在進行中,則不執行任何操作(並讓先前運行的任務完成)。

在這種情況下,“無所事事”意味着什么? 運行algorithmX完成后,線程應該阻塞並重試執行? 如果是這種情況,則應使用semaphore.acquire而不是tryAcquire,並且AtomicBoolean解決方案將無法按預期工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM