我试图在java中实现并发缓存以供学习建议。
此代码负责garantee线程安全操作。因此,每当线程尝试获取值时,如果此值尚未缓存,则算法应从最后一个缓存的值计算它。
我的问题是我得到了应该已经缓存的空值。我使用信号量(虽然我也尝试使用ReentrantLock,所以我认为这不是问题),以确保线程安全访问HashMap。
请注意,我想将锁定区域限制为尽可能小。所以我不想同步整个方法或利用已经线程安全的ConcurrentMap。
这是一个完整的简单代码:
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Semaphore;
public class ConcurrentCache {
private final Semaphore semaphore = new Semaphore(1);
private final Map<Integer, Integer> cache;
private int lastCachedNumber;
public ConcurrentCache() {
cache = new HashMap<Integer, Integer>();
cache.put(0, 0);
lastCachedNumber = 0;
}
public Integer fetchAndCache(int n) {
//if it's already cached, supposedly i can access it in an unlocked way
if (n <= lastCachedNumber)
return cache.get(n);
lock();
Integer number;
if (n < lastCachedNumber) { // check it again. it may be updated by another thread
number = cache.get(n);
} else {
//fetch a previous calculated number.
number = cache.get(lastCachedNumber);
if (number == null)
throw new IllegalStateException(String.format(
"this should be cached. n=%d, lastCachedNumber=%d", n,
lastCachedNumber));
for (int i = lastCachedNumber + 1; i <= n; i++) {
number = number + 1;
cache.put(i, number);
lastCachedNumber = i;
}
}
unlock();
return number;
}
private void lock() {
try {
semaphore.acquire();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private void unlock() {
semaphore.release();
}
public static void main(String[] args) {
ConcurrentCache cachedObject = new ConcurrentCache();
for (int nThreads = 0; nThreads < 5; nThreads++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int cacheValue = 0; cacheValue < 1000; cacheValue++) {
if (cachedObject.fetchAndCache(cacheValue) == null) {
throw new IllegalStateException(String.format(
"the number %d should be cached",
cacheValue));
}
}
}
}).start();
}
}
}
谢谢你的帮助。
答案 0 :(得分:1)
指点/想法很少:
1)在创建Map
时预先调整大小,以适应所有/未来的缓存值,Map
调整大小非常不安全且耗时
2)您可以将整个算法简化为
YourClass.get(int i) {
if (!entryExists(i)) {
lockEntry(i);
entry = createEntry(i);
putEntryInCache(i, entry);
unlockEntry(i);
}
return entry;
}
修改强>
另一点:
3)你的缓存方法非常糟糕 - 想象如果第一个请求要获得@ 1,000,000位置的东西会发生什么?
在单独的线程中预先填充会更好......