在很多场景中,对于一个对象,经常读多写少,为了优化性能,使用了缓存机制,缓存之所以如此重要,就是因为缓存的数据就是读多写少的,

并且针对缓存,我们在java中专门制定了一种并发的锁,读写锁(ReadWriteLock)

读写锁,顾名思义,就是遵循了允许多个线程同时读取共享变量,但只有一个线程可以对变量进行写操作 在写的时候,不允许读

读写锁最大的进步就是读写锁允许多个线程同时读取变量,但是写的操作是互斥的

在Java中,关于ReadWirteLock来说是一个接口,实现类是ReentrantReadWriteLock,支持可重入的读写锁

接下来这段代码,在结合了读写锁的基础上使用了懒加载机制,如果缓存中没有缓存目标对象,从数据库中加载并写入,写入用到了写锁

class Cache<K,V> {

final Map<K, V> m =

new HashMap<>();

final ReadWriteLock rwl =

new ReentrantReadWriteLock();

final Lock r = rwl.readLock();

final Lock w = rwl.writeLock();

V get(K key) {

V v = null;

//读缓存

r.lock(); ①

try {

v = m.get(key); ②

} finally{

r.unlock(); ③

}

//缓存中存在,返回

if(v != null) { ④

return v;

}

//缓存中不存在,查询数据库

w.lock(); ⑤

try {

//再次验证

//其他线程可能已经查询过数据库

v = m.get(key); ⑥

if(v == null){ ⑦

//查询数据库

//v=省略查询代码

m.put(key, v);

}

} finally{

w.unlock();

}

return v;

}

}

在这段代码中,在获取到写锁的时候,并没有直接去查询数据库,而是又一次的查询了缓存

这是为了避免在使用到锁的时候,前几个获取到的线程已经加载进入缓存了

避免了过多的IO操作

读写锁的一个特有的个性就是支持锁的降级

首先说有一种应用场景,就是先进行读取,读取完成后发现没有,在进行查询写入

换成读写锁的场景就是,先获取到了读锁,在升级为写锁

在读写锁中,对于升级是不支持的,上述的流程会发生错误

但是支持锁的降级,可以在获取到写锁的同时拿到读锁

class CachedData {

Object data;

volatile boolean cacheValid;

final ReadWriteLock rwl =

new ReentrantReadWriteLock();

// 读锁

final Lock r = rwl.readLock();

//写锁

final Lock w = rwl.writeLock();

void processCachedData() {

// 获取读锁

r.lock();

if (!cacheValid) {

// 释放读锁,因为不允许读锁的升级

r.unlock();

// 获取写锁

w.lock();

try {

// 再次检查状态

if (!cacheValid) {

data = …

cacheValid = true;

}

// 释放写锁前,降级为读锁

// 降级是可以的

r.lock(); ①

} finally {

// 释放写锁

w.unlock();

}

}

// 此处仍然持有读锁

try {use(data);}

finally {r.unlock();}

}

}

在这里就是支持在获取到写锁的时候获取读锁

读写锁,在Java中的实现,是和可重入锁一致的,在构造函数中,支持公平模式和非公平模式

但是,对于读写锁,只有写锁支持了条件变量,读锁是不支持条件变量的,读锁调用 newCondition会抛出UnsupportedOperationException异常

发表评论

邮箱地址不会被公开。 必填项已用*标注