2017-07-17 31 views
0

我正在使用wait做一個關於Java併發的練習,並通知學習考試。 考試將被編寫,所以代碼必須是完美的,因爲我們不能嘗試編譯和檢查錯誤。Java併發練習。異步下載

這是練習題的文本:

ex 總體思路:

  • 當下載器實例化隊列和HashMap中創建並傳遞給所有的線程。 (共享數據)
  • 下載方法將url添加到隊列並調用notifyAll來喚醒Downloader線程。
  • getData方法會等待,直到所提供的url的哈希映射中有數據。當數據可用時,它返回給調用者。
  • 下載器線程運行無限循環。它一直等到隊列中存在一個URL。當它接收到一個url時,它會下載它並將這些字節放在hashmap中調用notifyAll來喚醒可能在getData方法中等待的用戶。

這是我公司生產的代碼:

public class Downloader{ 

    private Queue downloadQueue; 
    private HashMap urlData; 
    private final static THREADS_NUMBER; 

    public Downloader(){ 
     this.downloadQueue = new Queue(); 
     this.urlData = new HashMap(); 
     for(int i = 0; i < THREADS_NUMBER; i++){ 
      new DownTh(this.downloadQueue, this.urlData).start(); 
     } 
    } 

    void syncronized download(String URL){ 
     downloadQueue.add(url); 
     notifyAll(); 
     return; 
    } 

    byte[] syncronized getData(String URL){ 
     while(urlData.get(URL) == null){ 
      wait() 
     } 

     return urlData.get(URL); 
    } 
} 

public class DownTh extend Thread{ 

    private Queue downloadQueue; 
    private HashMap urlData; 

    public DownTh(Queue downloadQueue, HashMap urlData){ 
     this.downloadQueue = downloadQueue 
     this.urlData = urlData; 
    } 

    public void run(){ 
     while(true){ 
      syncronized{ 
       while(queue.isEmpty()){ 
        wait() 
       } 

       String url = queue.remove(); 
       urlData.add(url, Util.download(url)) 

       notifyAll()    
      } 
     } 
    } 
} 

你能幫助我,並告訴我如果邏輯是正確的?

回答

0

讓我們假設所有那些處理同步的Java類都不存在,因爲這是一個綜合性任務,並且您要處理的只有sychronizedwaitnotify

用簡單的話來回答的第一個問題是:「誰會等待什麼?」

  • 下載線程將等待URL下載。
  • 調用者將等待該下載線程的結果。

這是什麼意思?我們需要在調用者和下載線程(您的urlData)之間至少有一個同步元素,並且爲方便起見,還應該有一個數據對象處理下載數據,並檢查下載是否尚未完成。

所以會發生的具體步驟:

  1. 來電請求新的下載。
    create:DownloadResult
    write:urlData(url - > DownloadResult)
    喚醒urlData上的1個線程。

  2. 線程X必須找到數據來下載並處理它或/然後再次入睡。
    讀:urlData(找到第一未處理DownloadResult,否則等待urlData)
    寫:DownloadResult(獲取它)
    寫:DownloadResult(下載結果)
    通知:任何人在等待DownloadResult
    重複

  3. 來電必須能夠異步檢查/等待下載結果。
    讀:urlData
    讀:DownloadResult訪問對象urlData或DownloadResult時(上DownloadResult如果需要等待)

由於有讀取並從在這些對象上的不同線程寫入,同步是必需的。

又會有一個等待/通知協會:

  • 主叫 - > urlData - > DownTh
  • DownTh - > DownloadResult - >主叫

經過仔細分析下面的代碼會滿足要求:

public class DownloadResult { 

    protected final URL url; // this is for convenience 
    protected boolean inProgress; 
    protected byte[] result; 

    public DownloadResult(final URL url) { 
    this.url = url; 
    this.inProgress = false; 
    } 

    /* Try to lock this against tother threads if not already acquired. */ 
    public synchronized boolean acquire() { 
    if (this.inProgress == false) { 
     this.inProgress = true; 
     return true; 
    } else { 
     return false; 
    } 
    } 

    public void download() { 
    final byte[] downloadedBytes = Util.download(this.url); // note how this is done outside the synchronized block to avoid unnecessarily long blockings 
    synchronized (this) { 
     this.result = downloadedBytes; 
     this.notifyAll(); // wake-up ALL callers 
    } 
    } 

    public synchronized byte[] getResult() throws InterruptedException { 
    while (this.result == null) { 
     this.wait(); 
    } 
    return this.result; 
    } 

} 

protected class DownTh extends Thread { 

    protected final Map<URL, DownloadResult> urlData; 

    public DownTh(final Map<URL, DownloadResult> urlData) { 
    this.urlData = urlData; 
    this.setDaemon(true); // this allows the JVM to shut down despite DownTh threads still running 
    } 

    protected DownloadResult getTask() { 
    for (final DownloadResult downloadResult : urlData.values()) { 
     if (downloadResult.acquire()) { 
     return downloadResult; 
     } 
    } 
    return null; 
    } 

    @Override 
    public void run() { 
    DownloadResult downloadResult; 
    try { 
     while (true) { 
     synchronized (urlData) { 
      while ((downloadResult = this.getTask()) == null) { 
      urlData.wait(); 
      } 
     } 
     downloadResult.download(); 
     } 
    } catch (InterruptedException ex) { 
     // can be ignored 
    } catch (Error e) { 
     // log here 
    } 
    } 
} 

public class Downloader { 

    protected final Map<URL, DownloadResult> urlData = new HashMap<>(); 

    // insert constructor that creates the threads here 

    public DownloadResult download(final URL url) { 
    final DownloadResult result = new DownloadResult(url); 
    synchronized (urlData) { 
     urlData.putIfAbsent(url, result); 
     urlData.notify(); // only one thread needs to wake up 
    } 
    return result; 
    } 

    public byte[] getData(final URL url) throws InterruptedException { 
    DownloadResult result; 
    synchronized (urlData) { 
     result = urlData.get(url); 
    } 
    if (result != null) { 
     return result.getResult(); 
    } else { 
     throw new IllegalStateException("URL " + url + " not requested."); 
    } 
    } 
} 

在真正的Java中,事情將是d換句話說,通過使用Concurrent類和/或Atomic類,所以這僅僅是爲了教育目的。有關進一步閱讀,請參閱「可調用的未來」。