开发中我们经常使用到缓存,第一次请求的时候从数据库中获取数据,然后保存到缓存中,这样下次访问的时候就可以直接从缓存中返回数据了,代码如下
/**
* 缓存设计
*
* @Author donny
* @Date 2022/9/1
*/
public class CachedUtil {
/**
* 缓存
*/
public static Map MAP_CACHE = new HashMap<>();
/**
* 查询数据
* @param key
* @return
*/
public Object getData(String key) {
Object value = MAP_CACHE.get(key);
if (value == null) {
value = loadDB(key);
MAP_CACHE.put(key, value);
}
return value;
}
/**
* 模拟从数据库获取数据
*
* @param key
* @return
*/
private Object loadDB(String key) {
return new Object();
}
} 但是如果某个热点key,在不停地扛着大并发,在这个key失效的瞬间,持续的大并发请求就会击破缓存,直接请求到数据库,造成缓存击穿:
public Object getData(String key) {
Object value = MAP_CACHE.get(key);
// 大并发的请求某个key,如果该key在缓存中不存在,就会直接请求到数据库,造成缓存击穿
if (value == null) {
value = loadDB(key);
MAP_CACHE.put(key, value);
}
return value;
}很自然的我们想到加一个锁来解决这个问题:
public static Lock lock = new ReentrantLock();
public Object getData(String key) {
lock.lock();
try {
Object value = MAP_CACHE.get(key);
if (value == null) {
value = loadDB(key);
MAP_CACHE.put(key, value);
}
return value;
} finally {
lock.unlock();
}
}但是一旦加锁后所有的访问都变成了串行访问,为了提高效率,我们决定改成读写锁来实现:
public static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public Object getData(String key) {
// 使用读锁
readWriteLock.readLock().lock();
try {
Object value = MAP_CACHE.get(key);
if (value == null) {
// 如果缓存中不存在则取消读锁,改用写锁
readWriteLock.readLock().unlock();
readWriteLock.writeLock().lock();
try {
value = loadDB(key);
MAP_CACHE.put(key, value);
} finally {
// 通过在释放写锁之前获取读锁进行降级
readWriteLock.readLock().lock();
// 解锁写锁,此时仍然持有读锁
readWriteLock.writeLock().unlock();
}
}
return value;
} finally {
readWriteLock.readLock().unlock();
}
}但是这样写的话还是存在一个小问题:
// 多个线程排队获取写锁
readWriteLock.writeLock().lock();
try {
// 可能前面获取到写锁的线程已经将值写入了缓存,所以此处还需要再次判断下value是否为null
value = loadDB(key);
MAP_CACHE.put(key, value);
} 最终改写后的代码:
public Object getData(String key) {
// 使用读锁
readWriteLock.readLock().lock();
try {
Object value = MAP_CACHE.get(key);
if (value == null) {
// 如果缓存中不存在则取消读锁,改用写锁
readWriteLock.readLock().unlock();
// 多个线程排队获取写锁
readWriteLock.writeLock().lock();
try {
// 可能前面获取到写锁的线程已经将值写入了缓存,所以需要再次判断value是否为null
value = MAP_CACHE.get(key);
if (value == null) {
value = loadDB(key);
MAP_CACHE.put(key, value);
}
} finally {
// 通过在释放写锁之前获取读锁进行降级
readWriteLock.readLock().lock();
// 解锁写锁,此时仍然持有读锁
readWriteLock.writeLock().unlock();
}
}
return value;
} finally {
readWriteLock.readLock().unlock();
}
}总结:
| 留言与评论(共有 0 条评论) “” |