ReadWriteLock缓存
读写锁使用原则:
一个共享变量允许同时被多个读线程读取到
一个共享变量在同一时刻只能被一个写线程进行写操作
一个共享变量在被写线程执行写操作时,这个共享变量不能被读线程执行读操作
在读写锁中,读锁调用newCondition()会抛出UnsupportedOperationException异常,读锁不支持条件变量。
按需查询缓存的业务
class ReadWriteLockCache<K,V> { private final Map<K, V> m = new HashMap<>(); private final ReadWriteLock rwl = new ReentrantReadWriteLock(); private final Lock r = rwl.readLock(); private 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; } }
get()方法中从缓存中读取数据,对查询缓存的操作添加读锁,查询返回后,进行解锁操作。判断缓存中返回的数据是否为空,不为空,则直接返回数据;如果为空,则获取写锁,之后再次从缓存中读取数据,如果缓存中不存在数据,则查询数据库,将结果数据写入缓存,释放写锁。最终返回结果数据
如果有三个线程同时调用get()方法,同时运行到 w.lock(),由于写锁的排他性。只有一个线程会获取到写锁,其他两个线程则阻塞在w.lock()处。获取到写锁的线程继续往下执行查询数据库,将数据写入缓存,之后释放写锁。另外两个线程竞争写锁,某个线程会获取到锁,继续往下执行,如果在w.lock()后没有v = m.get(key); 再次查询缓存的数据,则这个线程会直接查询数据库,将数据写入缓存后释放写锁。
在ReadWriteLock中,锁是不支持升级的,因为读锁还未释放时,此时获取写锁,就会导致写锁永久等待,相应的线程也会被阻塞而无法唤醒。
class CachedData { Object data; volatile boolean cacheValid; final 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(); try { // 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(); } finally { rwl.writeLock().unlock(); // Unlock write, still hold read } } try { use(data); } finally { rwl.readLock().unlock(); } } }}
立志如山 静心求实
浙公网安备 33010602011771号