簡體   English   中英

Java讀寫鎖定要求,具有來自不同線程的鎖定和釋放

[英]Java read & write lock requirement, with lock and release from different threads

我試圖找到一個不那么笨重的Java並發問題的解決方案。

問題的關鍵在於,當仍有工作線程處於活動狀態時,我需要對塊進行關閉調用,但關鍵的方面是每個工作任務都是異步生成和完成的,因此保持和釋放必須由不同的線程完成。 一旦他們的工作完成,我需要他們以某種方式向關閉線程發送信號。 只是為了讓事情更有趣,工作線程不能互相阻塞,所以我不確定信號量在這個特定實例中的應用。

我有一個解決方案,我認為安全地完成了這項工作,但是我對Java並發工具的不熟悉使我認為可能有一個更簡單或更優雅的模式。 在這方面的任何幫助將不勝感激。

這是我到目前為止,除了評論之外相當稀疏:

final private ReentrantReadWriteLock shutdownLock = new ReentrantReadWriteLock();
volatile private int activeWorkerThreads;
private boolean isShutdown;

private void workerTask()
{
   try
   {
      // Point A: Worker tasks mustn't block each other.
      shutdownLock.readLock().lock();

      // Point B: I only want worker tasks to continue if the shutdown signal
      // hasn't already been received.
      if (isShutdown)
         return;

      activeWorkerThreads ++;

      // Point C: This async method call returns immediately, soon after which
      // we release our lock. The shutdown thread may then acquire the write lock
      // but we want it to continue blocking until all of the asynchronous tasks
      // have completed.
      executeAsynchronously(new Runnable()
      {
         @Override
         final public void run()
         {
            try
            {
              // Do stuff.
            }
            finally
            {
               // Point D: Release of shutdown thread loop, if there are no other
               // active worker tasks.
               activeWorkerThreads --;
            }
         }
      });
   }
   finally
   {
      shutdownLock.readLock().unlock();
   }
}


final public void shutdown()
{
   try
   {
      // Point E: Shutdown thread must block while any worker threads
      // have breached Point A.
      shutdownLock.writeLock().lock();

      isShutdown = true;

      // Point F: Is there a better way to wait for this signal?
      while (activeWorkerThreads > 0)
         ;

      // Do shutdown operation.
   }
   finally
   {
      shutdownLock.writeLock().unlock();
   }
}

在此先感謝您的幫助!

拉斯

將activeWorkerThreads聲明為volatile不允許您執行activeWorkerThreads ++,因為++只是簡寫,

activeWorkerThreads = activeWorkerThreads + 1;

這不是原子的。 請改用AtomicInteger。

executeAsynchronously()是否將作業發送到ExecutorService? 如果是這樣,你可以使用awaitTermination方法,所以你的關閉鈎子將是,

executor.shutdown();
executor.awaitTermination(1, TimeUnit.Minutes); 

ExecutorService應該是提到的sbridges的首選解決方案。

作為替代方案,如果工作線程的數量是固定的,那么您可以使用CountDownLatch

final CountDownLatch latch = new CountDownLatch(numberOfWorkers);

latch傳遞給每個工作線程,並在任務完成時調用latch.countDown()

從主線程調用latch.await()以等待所有任務完成。

哇哇 永遠不要這樣做:

  // Point F: Is there a better way to wait for this signal?
  while (activeWorkerThreads > 0)
     ;

你正在旋轉並消耗CPU。 使用適當的通知:

首先: synchronize一個對象, 然后檢查activeWorkerThreads,並在對象上wait()如果它仍然> 0):

synchronized (mutexObject) {
    while (activeWorkerThreads > 0) {
        mutexObject.wait();
    }
}

第二步:讓工作人員在減少activeWorkerThreads計數后通知()該對象。 您必須在調用notify之前同步對象。

synchronized (mutexObject) {
    activeWorkerThreads--;
    mutexObject.notify();
}

第三:每當你觸摸activeWorkerThreads時,看到你(在實現1和2之后)同步一個對象,使用它作為保護; 變量不需要是volatile。

然后:您用作控制對activeWorkerThreads訪問的互斥鎖的同一對象也可用於控制對isShutdown的訪問。 例:

synchronized (mutexObject) {
    if (isShutdown) {
        return;
    }
}

除了不可思議的少量時間之外,這不會導致工作人員相互阻擋(無論如何,您可能無法避免使用讀寫鎖定)。

您可以在此方案中使用信號量,而不需要忙等待shutdown()調用。 想到它的方法是作為一套門票發給工人,表明他們在飛行中。 如果shutdown()方法可以獲取所有票證,那么它知道它已經耗盡了所有工作人員並且沒有活動。 因為#acquire()是一個阻塞調用,所以shutdown()不會旋轉。 我已經將這種方法用於分布式主工作庫,並且可以輕松擴展它以處理超時和重試。

Executor executor = // ...
final int permits = // ...
final Semaphore semaphore = new Semaphore(permits);

void schedule(final Runnable task) {
  semaphore.acquire();
  try {
    executor.execute(new Runnable() {
      @Override public run() {
        try {
          task.run();
        } finally {
          semaphore.release();
        }
      }
    });
  } catch (RejectedExecutionException e) {
    semaphore.release();
    throw e;
  }
}

void shutDown() {
  semaphore.acquireUninterruptibly(permits);

  // do stuff
}

這更像是對sbridges回答的評論,但作為評論提交有點太長了。

無論如何,只有1條評論。

當您關閉執行程序時,如果您使用默認實現(如Executors.newSingleThreadExecutor() ),則向執行程序提交新任務將導致未選中RejectedExecutionException 因此,在您的情況下,您可能希望使用以下代碼。

碼:

new ThreadPoolExecutor(1,
                       1,
                       1,
                       TimeUnit.HOURS,
                       new LinkedBlockingQueue<Runnable>(),
                       new ThreadPoolExecutor.DiscardPolicy());

這樣,在調用shutdown()之后提交給執行程序的任務就會被忽略。 上面的參數(1,1 ...等)應該生成一個執行器,它基本上是一個單線程執行器,但不會拋出運行時異常。

暫無
暫無

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

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