2015-09-04 75 views
3

我讀的Java併發的話題,這裏就是作者寫道:在Java中死鎖(用一個例子)

因爲的addListener()的removeListener(),和的UpdateProgress()都是同步的, 多個線程可以調用它們而不用踩在彼此的腳趾上。但是 這個代碼中潛伏着一個陷阱,可能會導致死鎖,即使只有一個鎖使用了 。 問題是,updateProgress()調用一個外來方法 - 它知道 沒有關係的方法。該方法可以做任何事情,包括獲得另一個鎖定。如果是這樣,那麼我們已經獲得了兩把鎖,卻不知道我們是否按照正確的順序完成了 。正如我們剛剛看到的那樣,這可能導致僵局。

能否請您解釋一下,我們如何能夠以錯誤的順序獲得兩把鎖,如果我們總是獲得放在第一位第一鎖定(作爲方法的UpdateProgress是同步的!),這意味着從兩個鎖第二作者談到將永遠被收購在第二位?

class Downloader extends Thread { 
    private InputStream in; 
    private OutputStream out; 
    private ArrayList<ProgressListener> listeners; 

    public Downloader(URL url, String outputFilename) throws IOException { 
     in = url.openConnection().getInputStream(); 
     out = new FileOutputStream(outputFilename); 
     listeners = new ArrayList<ProgressListener>(); 
    } 

    public synchronized void addListener(ProgressListener listener) { 
     listeners.add(listener); 
    } 

    public synchronized void removeListener(ProgressListener listener) { 
     listeners.remove(listener); 
    } 

    private synchronized void updateProgress(int n) { 
     for (ProgressListener listener : listeners) 
      listener.onProgress(n); 

    } 

    public void run() { 
     int n = 0, total = 0; 
     byte[] buffer = new byte[1024]; 
     try { 
      while ((n = in.read(buffer)) != -1) { 
       out.write(buffer, 0, n); 
       total += n; 
       updateProgress(total); 
      } 
      out.flush(); 
     } catch (IOException e) { 
     } 
    } 

} 

回答

3

比方說,你有Downloader兩個實例,叫他們dlAdlB。你也有一個監聽器,它只是來自那些下載者刪除自身,以及最初連接到兩個下載:

Downloader dlA = new Downloader(...); 
Downloader dlB = new Downloader(...); 

ProgressListener listener = new ProgressListener() { 
    @Override 
    public void onProgress(int n) { 
    dlA.removeListener(this); 
    dlB.removeListener(this); 
    } 
} 

dlA.addListener(listener); 
dlB.addListener(listener); 

好了,現在該怎麼辦,當兩個線程在同一時間打電話updateProgress發生,一個在dlA另一個是dlB

thread1: dlA.updateProgress(1)  thread2: dlB.updateProgress(1) 
     gets lock on dlA     gets lock on dlB 
     calls listener.onProgress(1)  calls listener.onProgress(1) 
      calls dlA.removeListener(this)  calls dlA.removeListener(this) 
      succeeds       tries to get lock on dlA 
      calls dlB.removeListener    (stuck until thread1 finishes 
      tries to get lock on dlB    dlA.updateProgress) 
      (stuck until thread2 finishes 
       dlB.updateProgress) 

你走了,僵局!

的問題是,聽衆代碼可以做任何它想做,包括獲取鎖(直接或間接),雖然你的線程一直保持在this執行代碼。當你沒有完全控制獲得的鎖時,你會打開自己的死鎖的可能性。

+0

謝謝。 「監聽器調用」動作發生在單獨的線程中(即線程3,線程4與線程1,2並行),還是發生在監聽器的onProgress()方法中? – CoolOne

+0

好吧,兩者。它發生在偵聽器的'onProgress()'方法中,但是請記住有_two_偵聽器,每個偵聽器都在獨立線程上調用onProgress。在thread1上調用dlA的監聽器(通過在thread1上調用的dlA.updateProgress中的listener.onProgress(n)調用),並且在thread2上調用dlB的監聽器。但是,你甚至可以用_one_ listener來完成這個工作,它將自己從dlA和dlB中移除。 – yshavit

+0

我稍微更新了答案,以顯示只有一個聽衆會發生什麼情況。 – yshavit

0

原因是listener.onProgress()超出了您的鎖定範圍。如果它試圖獲得對已經被阻塞的東西的鎖定,但是某些東西需要鎖定對象,則會發生死鎖。