突然发现之前写的自己实现XXX的话题不是很被大众关注,可能是真的写的不行,也可能是大多都是一些吃了饭没事做瞎写的一些东西,大家都没兴趣,之后可能会尽量写一些真正实用的东西,大家一起学习。言归正传,促使我研究这个话题的原因是在工作中遇到需要自己实现多级缓存的情况。比如在springboot中我们虽然可以随意替换缓存技术,可以使用redis也可以使用ehcache,但是据我所知,这些缓存默认都是只能使用一种。假设现在我需要同时使用ehcache和redis,其中ehcache做本地的第一级缓存。这里忽略可能用到的mybatis,hibernate那些操作数据库的orm框架的缓存,单独只考虑应用层面的缓存。当然在已经有springboot,ehcache,redis的情况下,通过自定义注解,切面拦截,组合ehcache和redis的两级缓存,其实并不是很困难。难点只在于细节部分,比如怎么让自己定义的注解和切面可以被springboot中的缓存注解开关@EnableCaching控制,又比如多点部署时怎么让ehcache本地缓存和redis集中式缓存实现同步,当然也有使用缓存的时候怎么加锁的问题。本话题重点讨论,在缓存使用时的加锁问题。
其实当我们刚开始学java的线程中同步工具时,我们就接触到synchronized,锁,读写锁等。其中在api文档的ReentrantReadWriteLock类里有一个例子,专门演示了读写锁,在一个缓存对象中的使用的例子如下:
class CachedData {
Object data;
volatile boolean cacheValid;
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
// Recheck state because another thread might have acquired
// write lock and changed state before we did.
if (!cacheValid) {
data = ...
cacheValid = true;
}
// Downgrade by acquiring read lock before releasing write lock
rwl.readLock().lock();
rwl.writeLock().unlock(); // Unlock write, still hold read
}
use(data);
rwl.readLock().unlock();
}
}
里面的读写锁,double check,锁降级等用的炉火纯青自是不用说的,我当时也是对此记忆犹新,如获至宝啊,感觉这段代码很适合装B,然后我在我的缓存实现代码里也准备这么玩,当我把这段代码复制过去,改成我需要的逻辑后,我就发现了问题。瞬间感觉这要是这么玩,可能装B不成那个啥的。然后我搜了搜网上别人的这种玩法,想看看别人是不是也有这种尴尬。然后搜索出来一大把如下面代码(百度搜索:java读写锁实现缓存)