首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Java并发性练习。异步下载

Java并发性练习。异步下载
EN

Stack Overflow用户
提问于 2017-07-17 09:29:01
回答 1查看 681关注 0票数 1

我正在做一个关于Java并发性的练习,使用等待、通知来为考试做准备。考试将是书面的,所以代码必须是完美的,因为我们不能试图编译和检查错误。

这是这项工作的案文:

一般想法:

  • 在安装下载程序时,将创建队列和散列映射,并将其传递给所有线程。(共享数据)
  • 下载方法将url添加到队列中,并调用notifyAll来唤醒Downloader。
  • getData方法等待到提供的url的hashmap中有数据。当数据可用时,它将返回给调用方。
  • 下装载机线程运行无穷大循环。它等待到队列中有一个url。当它收到一个url时,它会下载它,并将字节放在hashmap中,调用notifyAll来唤醒可能在getData方法中等待的用户。

这是我编写的代码:

代码语言:javascript
复制
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()             
            }
        }
    }
}

你能帮我,告诉我逻辑是否正确吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-07-17 11:48:06

让我们先假设Java中所有处理同步的伟大类都不存在,因为这是一个合成任务,您需要处理的只有sychronizedwaitnotify

用简单的话回答的第一个问题是:“谁会等待什么?”

  • 下载线程将等待一个URL下载。
  • 调用者将等待下载线程的结果。

这究竟意味着什么呢?我们需要调用方和下载线程(您的urlData)之间至少有一个同步元素,也应该有一个数据对象处理下载数据本身,以便方便,并检查下载是否已经完成。

因此,将要发生的详细步骤是:

  1. 来电者请求新下载。 创建: DownloadResult 写: urlData(url -> DownloadResult) 唤醒urlData上的一个线程。
  2. 线程X必须找到数据才能下载和处理,否则就会再次入睡。 read: urlData (查找第一个未处理的DownloadResult,否则等待urlData) 写: DownloadResult (获取它) 写: DownloadResult (下载结果) 通知:任何等待DownloadResult的人 重复
  3. 调用方必须能够异步检查/等待下载结果。 读: urlData 阅读: DownloadResult (如有需要,请等待DownloadResult )

由于在这些对象上有来自不同线程的读写,所以在访问对象urlData或DownloadResult时需要同步。

此外,还将出现等待/通知关联:

  • 呼叫者-> urlData -> DownTh
  • DownTh -> DownloadResult ->调用者

在仔细分析之后,以下代码将满足这些要求:

代码语言:javascript
复制
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中,通过使用并发类和/或原子.上课,所以这只是为了教育目的。如需进一步阅读,请参阅“可调用的未来”。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45140516

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档