2 回答

TA貢獻1993條經驗 獲得超6個贊
該解決方案基于ConcurrentHashMap#computeIfAbsent,有兩個假設:
多個線程讀取同一個文件不是問題。
雖然文檔說由于阻塞,計算應該簡單而簡短,但我相信這只是相同密鑰(或存儲桶/條帶)訪問的問題,并且僅適用于更新(而不是讀取)?在這種情況下,這不是問題,因為我們要么成功計算了 value,要么 throw
IllegalArgumentException
。
使用它,我們通過將其作為放置密鑰所需的計算來實現每個密鑰只打開一次文件。
public String getLine(int lineNr) throws IllegalArgumentException {
if (lineNr > nrLines) {
throw new IllegalArgumentException();
}
return cache.computeIfAbsent(lineNr, (l) -> {
try (Stream<String> st = Files.lines(path)) {
Optional<String> optionalLine = st.skip(lineNr - 1).findFirst();
if (optionalLine.isPresent()) {
return optionalLine.get();
} else {
nrLines = nrLines > lineNr ? lineNr : nrLines;
throw new IllegalArgumentException();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
});
}
我通過產生 3 個線程來“驗證”第二個假設,其中:
Thread1 通過無限循環(永遠阻塞)來計算密鑰 0。
Thread2 嘗試放入鍵 0,但從未這樣做,因為 Thread1 阻塞。
Thread3 嘗試放入鍵 1,并立即這樣做。
試試看,也許它有效,或者假設是錯誤的,它很糟糕。Map 在內部使用存儲桶,因此即使使用不同的鍵,計算也可能成為瓶頸,因為它鎖定了存儲桶/條帶。

TA貢獻1946條經驗 獲得超4個贊
混ConcurrentMap在一起synchronized(this)可能不是正確的方法。包中的類java.util.concurrent是為特定用例設計的,并嘗試在內部優化同步。
相反,我建議先嘗試一個設計良好的緩存庫,看看性能是否足夠好。一個例子是咖啡因。根據Population docs,它為您提供了一種聲明如何加載數據的方法,即使是異步的:
AsyncLoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
// Either: Build with a synchronous computation that is wrapped as asynchronous
.buildAsync(key -> createExpensiveGraph(key));
// Or: Build with a asynchronous computation that returns a future
.buildAsync((key, executor) -> createExpensiveGraphAsync(key, executor));
添加回答
舉報