17.读写锁ReentrantWriteReadLock

2018-08-08  本文已影响0人  0x70e8

读写锁ReentrantWriteReadLock,基于AQS的锁机制,实现ReadWriteLock接口。内部有两个锁,一个读锁和一个写锁,将AQS的state分成高16位和低16位作为信号量分给这两个锁使用。

特性

读写锁的使用主要在于其读锁可以共享,使得读操作较多的操作能高效并发。

读写锁的使用

ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
Lock readLock = readWriteLock.readLock();
Lock writeLock = readWriteLock.writeLock();
readLock.lock();
try{
// 只读操作
}finally{
    readLock.unlock();
}


writeLock.lock();
try{
    //写操作
}finally{
    writeLock.unlock();
}
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();
     }
   }
 }

如果上面示例代码不使用锁降级,只在最后的finally中释放写锁,那么当数据在本线程中修改之后,其他的读线程并不能感知,因为他们都阻塞了,数据的改变能一直维持到此线程释放写锁,其他读线程才有可能读到此线程写的值,但仅仅是可能,因为此线程释放写锁后,可能一个写线程抢占了锁,又一次修改了值,那么这个线程的修改其实对其他读线程来说,可能是不可见的。

原理

读写锁的原理简单来说就是组合了AQS的独占锁和共享锁,并且其内部有一些协作,比如锁降级,写锁和读锁的互斥等。

上一篇 下一篇

猜你喜欢

热点阅读