[英]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.