讓我們假設所有那些處理同步的Java類都不存在,因爲這是一個綜合性任務,並且您要處理的只有sychronized
,wait
和notify
。
用簡單的話來回答的第一個問題是:「誰會等待什麼?」
- 下載線程將等待URL下載。
- 調用者將等待該下載線程的結果。
這是什麼意思?我們需要在調用者和下載線程(您的urlData
)之間至少有一個同步元素,並且爲方便起見,還應該有一個數據對象處理下載數據,並檢查下載是否尚未完成。
所以會發生的具體步驟:
來電請求新的下載。
create:DownloadResult
write:urlData(url - > DownloadResult)
喚醒urlData上的1個線程。
線程X必須找到數據來下載並處理它或/然後再次入睡。
讀:urlData(找到第一未處理DownloadResult,否則等待urlData)
寫:DownloadResult(獲取它)
寫:DownloadResult(下載結果)
通知:任何人在等待DownloadResult
重複
來電必須能夠異步檢查/等待下載結果。
讀: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類,所以這僅僅是爲了教育目的。有關進一步閱讀,請參閱「可調用的未來」。