2009-08-04 78 views
2

我有一個限制用戶一次下載n個文件的信號量。每個文件都在一個單獨的線程中下載。多次中斷信號量的問題

編輯:修改的例子,使得釋放正確執行

import java.util.concurrent.Semaphore; 
public void downloadFile() { 
    Thread downloadThread = new Thread() { 
     boolean bSemaphoreAcquired = false; 
     public void run() { 
      try { 
       semaphore.acquire(); 
       bSemaphoreAcquired = true; 
       // do the download 
      } catch (InterruptedException e) { 
      } finally { 
       if (bSemaphoreAcquired) semaphore.release(); 
      } 
     } 
    }; 
    // add download thread to the list of download threads 
    downloadThread.start(); 
} 

現在,任何新的下載將等待前一個下載完成一次信號的所有許可證已被收購。

當用戶選擇取消正在等待獲取調用的下載時,我調用interrupt()方法來終止該下載線程。 我面臨的問題是,一旦這個信號被中斷,它不會拋出InterruptedException異常第二次!任何創建的新線程都會等待獲取方法永遠!

Sequence of events (Semaphore that permits 2 threads) 
- thread 1 downloading 
- thread 2 downloading 
- thread 3 waiting on acquire() 
- thread 3 is cancelled (interrupt() is called). InterruptedException is thrown and the thread exits 
- thread 4 is created and is now waiting on acquire() 
- thread 4 is cancelled (interrupt() is called). InterruptedException IS NOT THROWN ANYMORE!!! 

這是線程4

Semaphore$FairSync(AbstractQueuedSynchronizer).fullGetFirstQueuedThread() line: 1276  
Semaphore$FairSync(AbstractQueuedSynchronizer).getFirstQueuedThread() line: 1232  
Semaphore$FairSync.tryAcquireShared(int) line: 201 
Semaphore$FairSync(AbstractQueuedSynchronizer).acquireSharedInterruptibly(int) line: 1142 
Semaphore.acquire() line: 267 
FileDownloadManager$1.run() line: 150 

的堆棧跟蹤爲什麼線程4未收到異常?

+1

這不符合您的問題有所幫助,但你應該把「釋放」調入finally塊,所以不存在信號泄漏是否應該有一個下載時出現異常。 – Thilo 2009-08-04 08:20:41

+0

如果第三次打斷它會怎麼樣?你確定它在等待「獲得」嗎?你能得到一個JVM線程轉儲(kill -QUIT)來確保這一點嗎? – Thilo 2009-08-04 08:35:48

+0

所有新線程都會一直等待獲取,一旦達到此狀態就不會中斷。現在,即使原始線程1和線程2完成執行並釋放信號量,新線程仍然無限期地等待acquire()!將堆棧跟蹤添加到問題中。 – Prashast 2009-08-04 08:44:22

回答

2

我會建議使用標準線程池而不是信號量。 解決方案的問題在於,無論您是否達到最大限制,您都會創建一個新線程。所以,如果你有1000個同時請求,你將創建1000個線程,這是非常浪費的。

而是嘗試這樣的事:

ExecutorService executorService = Executors.newFixedThreadPool(5); 
    executorService.submit(new Runnable() { 
     public void run() { 
      // do download 
     } 
    }); 
0

您是否在捕獲內釋放信號量?

爲什麼不把try-catch放在aquire-release中。不確定java是做什麼的,但不會更合乎邏輯。這樣,try ... catch中的任何問題總是以釋放的信號量結束。